简体   繁体   English

Jackson 注释 - 通过将索引映射到属性将数组解组为 object

[英]Jackson annotations - Unmarshall array as object by mapping index to property

I am reading from a stream that provides updates to an order book used to calculate market depth.我正在阅读 stream,它提供了用于计算市场深度的订单簿的更新。 Each includes a list of new entries for the order book.每个都包含订单簿的新条目列表。 Each entry contains three properties.每个条目包含三个属性。

  1. Market side (eg buy or sell)市场方面(例如买入或卖出)
  2. Quantity transacted of the commodity商品成交数量
  3. Unit price of the commodity in this transaction本次交易商品单价

These entries are represented in JSON as array nodes.这些条目在 JSON 中表示为数组节点。 Here is an example of how entries might be provided.以下是如何提供条目的示例。

{
    "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...
}

As you can see, the indices of each entry array are used as field names.如您所见,每个条目数组的索引用作字段名称。 The position of each array element defines what property it represents.每个数组元素的 position 定义了它代表什么属性。 The side is at index 0, the unit price is at index 1, and the quantity is at index 2.边在索引 0,单价在索引 1,数量在索引 2。

How can I de/serialize these using arrays Jackson annotations in Java 8?我如何使用 Java 8 中的 arrays Jackson 注释反序列化它们? I am only asking about the innermost arrays. I don't need help with the object structure in general.我只问最里面的 arrays。一般来说,我不需要 object 结构的帮助。

I tried making a class similar to the following.我尝试制作类似于以下内容的 class。

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;
    }
}

I have tried specifying @JsonFormat.Shape.ARRAY on the class. Every time I try to deserialize a sample string, I get an InvalidDefinitionException .我尝试在 class 上指定@JsonFormat.Shape.ARRAY 。每次我尝试反序列化示例字符串时,我都会得到一个InvalidDefinitionException

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException:线程“main”中的异常 com.fasterxml.jackson.databind.exc.InvalidDefinitionException:

Invalid type definition for type com.example.OrderBookEntry: Argument #0 has no property name, is not Injectable: can not use as Creator [constructor for com.example.OrderBookEntry, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] at [Source: (String)"["buy","470.84724800000004","16.14963"]";类型 com.example.OrderBookEntry 的类型定义无效:参数 #0 没有属性名称,不可注入:不能用作 Creator [com.example.OrderBookEntry 的构造函数,注释:{接口 com.fasterxml.jackson.JsonCrenotation. =@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] [来源:(String)]["buy","470.84724800000004","16.14963"]"; line: 1, column: 1]行:1,列:1]

Is there not a way to do this with only annotations?有没有办法只用注释来做到这一点?


PS (rant) PS(咆哮)

I'd just like to add, this is an absurd data structure.我只想补充一点,这是一个荒谬的数据结构。 It makes no sense.这没有道理。 The purpose of using array indices instead of object field names would be to reduce the size of messages.使用数组索引而不是 object 字段名称的目的是减少消息的大小。 This is a financial data stream, and any improvement to the.network latency of financial data is desirable.这是财务数据 stream,任何对财务数据网络延迟的改进都是可取的。 But, around each of these arrays is a completely superfluous wrapper object with a single named field.但是,围绕这些 arrays 的每一个都是一个完全多余的包装器 object,它只有一个命名字段。 This adds at least 10 bytes of unnecessary traffic per entry.这会为每个条目增加至少 10 字节的不必要流量。 The data structure has a very poor design.数据结构的设计非常糟糕。

There is more than one way to do this but I always prefer the combination of a dedicated class with a dedicated serializer.有不止一种方法可以做到这一点,但我总是更喜欢将专用 class 与专用序列化器结合使用。 Other options would be:其他选择是:

  • registering the serializer with ObjectMapper – explicit coding instead of (meta level) annotations使用 ObjectMapper 注册序列化程序——显式编码而不是(元级别)注释
  • use a general set method (with Map<String, Object>) – hides the serializer's aspect in a lengthy method使用通用设置方法(使用 Map<String, Object>)——在冗长的方法中隐藏序列化程序的方面
  • mapping JsonNode with setter in parent class – like #2将 JsonNode 与父 class 中的 setter 映射——如#2

Instead:反而:

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

And the serializer:和序列化器:

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.
    }

My example uses a package private deserializer class which works absolutely fine and locks it in its package (preferrably next to the OrderBookEntry).我的示例使用 package 私有反序列化器 class,它工作得很好,并将其锁定在其 package(最好在 OrderBookEntry 旁边)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM