【问题标题】:Jackson - Serialize only Ids of objects in a list attribute of a Java PojoJackson - 仅序列化 Java Pojo 的列表属性中的对象 ID
【发布时间】:2019-08-13 05:19:04
【问题描述】:

我想优化在线发送的 json 数据。我的代码中有三个模型。它们是客户、发票和详细信息。

客户类

@Data
public class Customer implements Serializable {

    private long customerId;

    private String name;

    private String taxId;

    private String phone;

    private String address;

    private String emailId;

    private Date created;

    private List<Invoice> invoices;
}

发票类

@Data
public class Invoice implements Serializable {

    private String invoiceId;

    private List<Particular> particulars;

    private Date invoiceDate;
}

特殊类

@Data
public class Particular {
    private String item;
    private int quantity;
    private float tax;
    private int unitPrice;
}

我的测试代码:

@Test
    public void makeCustomerJsonWithInvoices() throws JsonProcessingException {
        Customer customer = new Customer();
        customer.setCustomerId(1234);
        customer.setName("Pawan");
        customer.setPhone("+918989898989");
        customer.setEmailId("something@something.com");
        customer.setAddress("Mumbai, India");
        customer.setTaxId("MQZ11DPS");
        customer.setCreated(new Date());

        Invoice invoice1 = new Invoice();
        invoice1.setInvoiceId("A-1");
        Particular particular1 = new Particular("abc", 1, 0, 12);
        Particular particular2 = new Particular("xyz", 2, 0, 20);
        invoice1.setInvoiceDate(new Date());
        invoice1.setParticulars(Arrays.asList(particular1, particular2));

        Particular particular3 = new Particular("mno", 2, 0, 15);
        Invoice invoice2 = new Invoice();
        invoice2.setInvoiceId("A-2");
        invoice2.setParticulars(Arrays.asList(particular3));
        invoice2.setInvoiceDate(new Date());
        customer.setInvoices(Arrays.asList(invoice1, invoice2));

        String value = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
        System.out.println(value);
    }

我想要的是通过序列化 Invoice 来避免冗余,以便生成的 json 紧凑。这应该通过仅发送 invoiceId 属性值而不是整个 Invoice 对象 json 来实现。

测试代码打印的内容:

{
  "customerId" : 1234,
  "name" : "Pawan",
  "taxId" : "MQZ11DPS",
  "phone" : "+918989898989",
  "address" : "Mumbai, India",
  "emailId" : "something@something.com",
  "created" : 1553243962038,
  "invoices" : [ {
    "invoiceId" : "A-1",
    "particulars" : [ {
      "item" : "abc",
      "quantity" : 1,
      "tax" : 0.0,
      "unitPrice" : 12
    }, {
      "item" : "xyz",
      "quantity" : 2,
      "tax" : 0.0,
      "unitPrice" : 20
    } ],
    "invoiceDate" : 1553243962038
  }, {
    "invoiceId" : "A-2",
    "particulars" : [ {
      "item" : "mno",
      "quantity" : 2,
      "tax" : 0.0,
      "unitPrice" : 15
    } ],
    "invoiceDate" : 1553243962039
  } ]
}

我想要它打印的内容:

{
  "customerId" : 1234,
  "name" : "Pawan",
  "taxId" : "MQZ11DPS",
  "phone" : "+918989898989",
  "address" : "Mumbai, India",
  "emailId" : "something@something.com",
  "created" : 1553243962038,
  "invoices" : [ {
    "invoiceId" : "A-1"
  }, {
    "invoiceId" : "A-2"
  } ]
}

@Datalombok 注释,用于生成 getter 和 setter。

我尝试将 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "invoiceId") 注释添加到 Invoice 类,但这不会改变输出。

请注意,我希望这种带有 Invoice 的序列化仅在它作为子项传递给容器模型时发生。如果我想独立发送发票,它将序列化发票模型中的所有字段。我相信这是实现 RESTful WS 时的常见场景。

我需要为此编写客户序列化程序吗?

【问题讨论】:

  • 您可以使用@JsonIgnore 来忽略属性
  • 其实只是一个小例子。如果类结构很大,我可能需要在许多字段上注释 @JsonIgnore。

标签: java json jackson jackson2


【解决方案1】:

您可以在Invoice 类上使用@JsonAutoDetect 仅序列化invoiceId 字段,例如:

@JsonAutoDetect(
    fieldVisibility = Visibility.NONE,
    setterVisibility = Visibility.NONE,
    getterVisibility = Visibility.NONE,
    isGetterVisibility = Visibility.NONE,
    creatorVisibility = Visibility.NONE
)
@Data
public class Invoice implements Serializable {

    @JsonProperty (access = READ_ONLY)
    private String invoiceId;

    private List<Particular> particulars;

    private Date invoiceDate;
}

这将确保只有invoiceId 通过线路,请查看文档here

更新

如果您只想在Invoice 作为嵌套对象发送时具有此行为,那么您可以将其他字段设置为null(或首先不设置这些字段)并使用@JsonInclude 注释,例如:

@JsonInclude(Include.NON_NULL)
@Data
public class Invoice implements Serializable {
  ..
}

【讨论】:

  • 使用这种方法,当我想序列化整个模型(每个字段)时,我无法获取其他字段。请注意,我只想优化Invoice json,仅当它作为容器模型内的列表发送时(在我们的例子中为Customer)。
  • 这很脏,可能很危险。因此,您希望有一个将非 Id 字段显式设置为 null 的代码。如果在其他地方访问要发送的发票引用,这可能会导致一些副作用。
  • 如果不需要将这些字段传播到线路,我将有一个代码,首先不会 set 这些字段。
【解决方案2】:

您可以使用@JsonIgnore 忽略 JSON 响应中的属性。

或者你可以使用transient关键字来避免序列化

【讨论】:

【解决方案3】:

将以下 bean 视为对您的案例的轻微修改:

@Data
@JsonFilter("idOnlyFilter")
@AllArgsConstructor
class Complex {
    private String id;
    private List<String> aList;
    private Date aDate;
}

您可以使用@JsonFilter 概念在您想要的每个 bean 上定义真正精细的序列化条件。请特别注意ObjectMapper 配置以及@JsonFilter 注解中的过滤器名称idOnlyFilter

如下图所示:

@Test
public void includeOnlyOneField() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    FilterProvider filters = new SimpleFilterProvider()
            .addFilter("idOnlyFilter", SimpleBeanPropertyFilter.filterOutAllExcept("id"));

    Complex complex = new Complex("a string", List.of(), new Date());

    // when
    String complexAsString = mapper.writer(filters).writeValueAsString(complex);

    // then
    assertThat(complexAsString).isEqualTo("{\"id\":\"a string\"}");
}

【讨论】:

  • 这是相当笼统的。我不能假设我希望将它们的 id 序列化为具有名为 id 的属性的所有类。此外,我可能不想将此规则应用于某些课程。我们的代码中有很多地方是在内部进行序列化,我们无法访问 ObjectMapper。
【解决方案4】:

我可以通过以下方式修改 Customer 类来实现这一点。

@Data
public class Customer implements Serializable {

    private long customerId;

    private String name;

    private String taxId;

    private String phone;

    private String address;

    private String emailId;

    private Date created;

    @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="invoiceId")
    @JsonIdentityReference(alwaysAsId=true)
    private List<Invoice> invoices;
}

答案灵感来自https://stackoverflow.com/a/17583175/1365340

有了这个,我可以生成带有发票 ID 列表的 Customer json。 Invoice 对象在单独序列化时从 json 中的所有字段中获取所有值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-19
    • 1970-01-01
    相关资源
    最近更新 更多