【问题标题】:Special Field in JSON Data using Jackson使用 Jackson 的 JSON 数据中的特殊字段
【发布时间】:2014-12-31 03:23:22
【问题描述】:

我有一个具有特殊属性的数据结构。数据结构代表一整天的股票定价数据。 [所以基本上 OLHC] 在我当前的序列化例程和构建器中,我目前支持“全部”属性。这是构建器中的一种方法,它将所有 OHLC 价格设置为一个价格。

有没有办法设置它而不必拥有所有的属性名称?

  • 在序列化时,它应该进行比较并全部使用
  • 在反序列化时,它应该重定向到构建器中的 all 方法。

这在杰克逊有可能吗?

例子:

数据结构如下:

EODPrice 

  - Open
  - Low
  - Close
  - High

假设我们有 json:“{all: 5.00}”,它将反序列化为 Open:5.00,low:5.00,close:5.00,high:5.00。如果我们要序列化,我想改变行为,这样如果我们有 Open == Low == Close == High 那么我们将创建一个名为 all 的属性。如果该条件不成立,那么我们根本不会序列化“all”属性。

【问题讨论】:

  • 我只是想知道您是否可以提供更多详细信息。您如何期望all-property 被序列化/反序列化。你提到的比较是什么?
  • 我只是更改了描述以反映它
  • 我添加了一个处理序列化的答案。您希望反序列化如何工作? 它应该重定向到构建器中的 all 方法是什么意思?
  • all 方法在构建器中,但不在构建对象中。

标签: java json serialization jackson


【解决方案1】:

使用注释@JsonFilter 绝对可以实现序列化部分,您可以在Jackson documentation 中阅读该注释。

反序列化是标准的杰克逊使用@JsonCreator

过滤器可以应用于类、方法和字段,您可以编写自己的自定义过滤器来处理打开、低、关闭、高问题。

查看this tutorial 以获得出色的介绍。

有关代码示例,请查看此内容。首先,使用 @JsonFilter 注释声明您的 EODPrice

@JsonIgnoreProperties(ignoreUnknown = true) // required to skip the "all" attribute in the JSON
@JsonFilter("allFilter") // Specify the filter
public class EODPrice {
    private final BigDecimal close;
    private final BigDecimal high;
    private final BigDecimal low;
    private final BigDecimal open;

    // Builder method, does not include "all"
    @JsonCreator
    public EODPrice(
            @JsonProperty("open") final BigDecimal open,
            @JsonProperty("low") final BigDecimal low,
            @JsonProperty("close") final BigDecimal close,
            @JsonProperty("high") final BigDecimal high) {

        this.open = open;
        this.low = low;
        this.close = close;
        this.high = high;
    }

    // This is not part of the JSON but puts the business logic in the POJO
    @JsonIgnore
    public boolean allFieldsEqual() {
        return open.equals(low) && open.equals(close) && open.equals(high);
    }

    public BigDecimal getAll() {
        if (allFieldsEqual()) {
            return open;
        }
        return BigDecimal.ZERO;
    }

    public BigDecimal getClose() {
        return close;
    }

    public BigDecimal getHigh() {
        return high;
    }

    public BigDecimal getLow() {
        return low;
    }

    public BigDecimal getOpen() {
        return open;
    }
}

过滤器可能如下所示:

private PropertyFilter allFilter = new SimpleBeanPropertyFilter() {
    @Override
    public void serializeAsField(
            Object pojo,
            JsonGenerator jgen,
            SerializerProvider provider,
            PropertyWriter writer) throws Exception {

        // If it is not the "all" property, go on with normal serialization
        if (!writer.getName().equals("all")) {
            writer.serializeAsField(pojo, jgen, provider);
            return;
        }

        // Else, check the special all-rule
        final EODPrice eodPrice = (EODPrice) pojo;
        if (eodPrice.allFieldsEqual()) {
            // Only serialize if all fields are equal
            writer.serializeAsField(pojo, jgen, provider);
        }
    }

    @Override
    protected boolean include(BeanPropertyWriter writer) {
        return true;
    }

    @Override
    protected boolean include(PropertyWriter writer) {
        return true;
    }
};

最后,设置映射器。这个测试用例说明了过滤器的作用:

@Test
public void testJsonRoundtrip() throws IOException {
    final FilterProvider filters = new SimpleFilterProvider().addFilter("allFilter", allFilter);
    final EODPrice eodPriceWithAll = new EODPrice(BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE);
    final EODPrice eodPriceWithoutAll = new EODPrice(BigDecimal.TEN, BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE);

    final ObjectMapper mapper = new ObjectMapper();
    mapper.setFilters(filters);

    // First, test serialization
    final String eodWithAllAsStr = mapper.writeValueAsString(eodPriceWithAll);
    final String eodWithoutAllAsStr = mapper.writeValueAsString(eodPriceWithoutAll);

    Assert.assertTrue(eodWithAllAsStr.contains("all"));
    Assert.assertFalse(eodWithoutAllAsStr.contains("all"));

    // Then, test deserialization
    final EODPrice eodPriceWithAll2 = mapper.readValue(eodWithAllAsStr, EODPrice.class);
    final EODPrice eodPriceWithoutAll2 = mapper.readValue(eodWithoutAllAsStr, EODPrice.class);

    Assert.assertTrue(eodPriceWithAll2.allFieldsEqual());
    Assert.assertFalse(eodPriceWithoutAll2.allFieldsEqual());
}

编辑: 来自 OP 反序列化的更新被添加到 POJO。此外,业务逻辑从过滤器移至 POJO。

【讨论】:

  • 这就是我要找的!谢谢。我将不得不阅读并测试它,然后才能将其标记为答案。
  • 唯一担心的是:不可变对象中不存在 all 字段。 (这将在序列化时完成)
  • @monksy - 输入后答案已更新。我已将 all 字段的业务逻辑添加到不可变对象中。此外,我还添加了用于反序列化的代码(和测试代码)。
  • 我可以忍受。谢谢你的帮助!这是一个很好的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-23
  • 2011-08-29
相关资源
最近更新 更多