Spring Boot Jackson - 根据深度选择性序列化嵌入式对象属性

Posted

技术标签:

【中文标题】Spring Boot Jackson - 根据深度选择性序列化嵌入式对象属性【英文标题】:Spring Boot Jackson - Selectively serialize embedded object properties based on their depth 【发布时间】:2020-05-25 14:20:08 【问题描述】:

我已编辑问题以提供更清晰的说明。

我的问题陈述是如何自定义 Jackson 序列化在我的 REST 响应中发送的实体的方式,以使我的 JSON 不会变得太大/不可读?

考虑我有以下实体(或资源/模型等)

public class Department

    private Long id;
    private String name;
    private Employee head;
    /***
     * Other fields, getters and setters
     */

还有一个,像这样

public class Employee 
    private Long id;
    private String name;
    private Department department;
    /***
     * Other fields, getters and setters
     */    

我有一个 REST API,可以通过他/她的 ID 获取员工,这是我调用它时的响应

GET /api/employees/2

  "id":"2",
  "name":"John Doe",
  "department":
    "id":3,
    "name":"Product Support",
    "head":
      "id":1,
      "name":"The Chairman",
      "department":
        "id":1,
        "name":"Executive",
        "head":null
      
    
  

如您所见,响应很大(根据数据模型可能会变得非常大)。我想阻止序列化深入嵌套对象。

我更喜欢的 JSON 响应是:

GET /api/employees/2

  "id":"2",
  "name":"John Doe",
  "department":
    "id":3,
    "name":"Product Support",
  

我已经知道我可以在Department.head 上使用@JsonIgnore 注释,如下所示:

public class Department

    private Long id;
    private String name;

    @JsonIgnore
    private Employee head;
    /***
     * Other fields, getters and setters
     */

但是,这将永久跳过序列化 head 属性。例如,请参阅下面的 REST API 调用,添加 @JsonIgnore

GET /api/departments/3

   "id":3,
   "name":"Product Support",


// Here, I want the Full serialized form of the Department Object

所以,如您所见,问题是:当我调用GET /api/departments/3 时,我希望Department 对象被完全序列化。但是,当我调用GET /api/employees/2 时,我希望它跳过head 属性。在 Spring Boot 中使用时,如何在 Jackson 中实现这一点?

我还遇到了一个名为 Squiggly 的库,它使用类似 GraphQL 的查询语言来处理 API 响应,但我想知道是否可以不学习新库。

非常感谢

在 Abbin Varghese 回答后编辑 我将Employee 模型更改为以下模型:

public class Employee 
    private Long id;
    private String name;

    @JsonIgnoreProperties("head")
    private Department department;
    /***
     * Other fields, getters and setters
     */    

在序列化时,这给了我以下 JSON 输出:


  "id":"2",
  "name":"John Doe",
  "department":
    "id":3,
    "name":"Product Support",
  

这里的 pro 是它解决了我的问题,而无需为我的模型编写单独的 DTO 类。

然而,con 是这样的:在这种情况下,我试图仅针对 head 属性跳过序列化,因此将其包含在 @JsonIgnoreProperties 注释中是有意义的。如果要跳过的属性列表比要序列化的属性列表长怎么办?例如,如果我的 Department 有 15 个属性,并且我希望只序列化其中的 3 个,那么在 @JsonIgnoreProperties 注释中包含要跳过的 12 个属性变得很困难。

是否有相反的注释(仅包括这些字段等)??

【问题讨论】:

我建议定义单独的对象(避免使用 db 对象)以从 get 请求中返回数据。由于映射操作,它增加了一些复杂性,但它允许为每次调用选择要返回的数据。 @SergiiZhevzhyk 这绝对是我们正在考虑的事情。但是,我想要一些可以快速完成的事情,因为为每个实体创建镜像对象需要时间。考虑到我们现在已经采用的方法,有什么快速的解决方案或我可以采取的方向吗? @SergiiZhevzhyk 也就是说,即使我们创建单独的对象来从 get 请求返回数据,我的嵌套对象问题仍然存在?到那时,我的 JSON 响应将包含我的子对象的所有属性,无论我是否想要它们。 您需要类似 WarehouseResponse 和 ClientResponse 类的东西,它们之间不应有任何公共类。在这种情况下,可以控制 GET 方法中返回的内容。 【参考方案1】:

JsonProperty.Access.WRITE_ONLY 是避免元素的好方法。

例如..

@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@ManyToOne
@JoinColumn(name = "MODIFIED_BY")
private User lastUpdatedBy;

【讨论】:

除了“lastUpdatedBy”可用于反序列化之外,这与@JsonIgnore 不同吗?我试过这个。但是,我想包括“lastUpdatedBy”或根据对象的深度跳过它。 糟糕.. 我显然错过了真正具有挑战性的部分.. :).. 这有帮助吗:***.com/questions/60151132/… 从一开始,它似乎就有所帮助。我会试试看,让你知道!谢谢 嗨,Abbin,我尝试了链接,它解决了我的问题。不过,还有一些事情我可能需要帮助。我用我的发现编辑了我的问题。希望你能在这里帮助我!!

以上是关于Spring Boot Jackson - 根据深度选择性序列化嵌入式对象属性的主要内容,如果未能解决你的问题,请参考以下文章

spring-boot 使用啥版本的 Jackson?

Jackson 在我的 Spring Boot 应用程序中忽略了 spring.jackson.properties

如何在 Spring Boot 1.4 中自定义 Jackson

Spring Boot没有使用Jackson Kotlin插件

Spring Boot 未使用 Jackson Kotlin 插件

spring-boot用jackson改变json响应结构