【问题标题】:Jackson annotations - Unmarshall array as object by mapping index to propertyJackson annotations - 通过将索引映射到属性来将数组解组为对象
【发布时间】:2020-11-14 07:19:59
【问题描述】:

我正在从一个流中读取数据,该流为用于计算市场深度的订单簿提供更新。每个都包括订单簿的新条目列表。 每个条目包含三个属性。

  1. 市场方面(例如买入或卖出)
  2. 商品交易数量
  3. 本次交易商品的单价

这些条目在 JSON 中表示为数组节点。以下是如何提供条目的示例。

{
    "Changes": [
        { "entry": ["buy","470.84724800000004","16.14963"] },
        { "entry": ["buy","470.787392","0.01"] },
        { "entry": ["sell","473.112752","9.325423"] },
        { "entry": ["sell","473.052608","11.80723"] }
    ],
    ...some more fields; not relevant to this question...
}

如您所见,每个条目数组的索引都用作字段名称。 每个数组元素的位置定义了它所代表的属性。边在索引0,单价在索引1,数量在索引2。

如何在 Java 8 中使用数组 Jackson 注释对这些进行反序列化? 我只询问最里面的数组。我一般不需要对象结构方面的帮助。

我尝试创建一个类似于以下的类。

public class OrderBookEntry {
    final String side;
    final BigDecimal price;
    final BigDecimal quantity;

    @JsonCreator
    public OrderBookEntry(@JsonProperty(index = 0, required = true) String side,
                          @JsonProperty(index = 1, required = true) BigDecimal price,
                          @JsonProperty(index = 2, required = true) BigDecimal quantity) {
        this.side = side;
        this.price = price;
        this.quantity = quantity;
    }
}

我已经尝试在课堂上指定@JsonFormat.Shape.ARRAY。每次我尝试反序列化一个示例字符串时,我都会得到一个InvalidDefinitionException

线程“主”com.fasterxml.jackson.databind.exc.InvalidDefinitionException 中的异常:

com.example.OrderBookEntry 类型的类型定义无效:参数 #0 没有属性名称,不可注入:不能用作 Creator [com.example.OrderBookEntry 的构造函数,注释:{interface com.fasterxml.jackson. annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] 在 [Source: (String)"["buy","470.84724800000004","16.14963"]";行:1,列:1]

有没有办法只用注释来做到这一点?


附注(咆哮)

我想补充一下,这是一个荒谬的数据结构。毫无意义。使用数组索引而不是对象字段名称的目的是减少消息的大小。这是一个金融数据流,任何对金融数据网络延迟的改进都是可取的。但是,围绕这些数组中的每一个都是一个完全多余的包装对象,具有一个命名字段。这会为每个条目增加至少 10 个字节的不必要流量。数据结构的设计很差。

【问题讨论】:

    标签: java data-annotations json-deserialization jackson-databind


    【解决方案1】:

    有不止一种方法可以做到这一点,但我总是更喜欢将专用类与专用序列化程序相结合。其他选择是:

    • 使用 ObjectMapper 注册序列化程序 - 显式编码而不是(元级别)注释
    • 使用通用的 set 方法(使用 Map)——在冗长的方法中隐藏序列化程序的方面
    • 将 JsonNode 与父类中的 setter 进行映射——如 #2

    改为:

    @JsonDeserialize(using = OrderBookEntryDeserializer.class)
    public class OrderBookEntry {
      // no further Jackson-annotations necessary
    }
    

    还有序列化器:

    class OrderBookEntryDeserializer extends StdDeserializer<OrderBookEntry> {
        OrderBookEntryDeserializer() {
            super(OrderBookEntry.class);
        }
    
        @Override
        public OrderBookEntry deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            final JsonNode node = p.getCodec().readTree(p);
            // now follow Jackson API to turn the node into
            // an ArrayNode, there are plenty of is-methods
            // and it requires a hard cast.
        }
    

    我的示例使用了一个包私有反序列化器类,它工作得非常好,并将其锁定在它的包中(最好在 OrderBookEntry 旁边)。

    【讨论】:

    • 有没有办法在没有自定义反序列化器的情况下做到这一点?理想情况下只有注释?
    • 我认为没有。假设普通程序员只是将这个模式映射到一个数组中,然后用纯 Java 对其进行转换。这种将数组索引映射到属性的方式非常不寻常——你已经在你的文章中指出了这一点。 (我真的很感激避免所有更改架构的建议)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 2014-12-03
    • 2020-02-10
    • 2012-01-23
    • 2019-05-11
    • 2011-08-21
    • 2021-04-18
    相关资源
    最近更新 更多