使用 Jackson 将巨大的 JSON 响应反序列化为 POJO 的最有效方法是什么?

What is the most efficient way to deserialize huge JSON responses into POJOs using Jackson?

我有一个 Vert.x/Java Web 服务,它对另一个服务进行 REST 调用并接收类似于以下内容的分页响应:

{
  "pagination": {
    "count": 500,
    "totalPages": 279,
    "totalResources": 139255,
    "next": "https://some-service.com/some-endpoint?next=(some-hash)"
  },
  "objects": [
   {
     // About 200 fields per "object" mixed in various levels of nesting,
     // only about 10 of which I want to deserialize
     "model": {
       "A field": "A value"
     },
     "topLevelField": "value",
     "someMoreNestedData": {
       // ...
     }
   }
  ]
}

500 "objects" 的最大响应大小约为 4-5MB,或大约 10 万行文本。收到响应后,我想使用 Jackson 将对象数组反序列化为一个扁平化数据模型,该模型由我的系统关心的每个对象的 10 个字段组成,丢弃其余部分。我还需要在'pagination'节点记录分页信息。

我已经使用两个与这些非常相似的 类 实现了反序列化(省略了 Lombok 注释以保存 space):

@JsonIgnoreProperties(ignoreUnknown = true)
public class RawResponse {
    @JsonIgnore
    private Integer count;
    @JsonIgnore
    private Integer totalPages;
    @JsonIgnore
    private Integer totalResources;
    @JsonIgnore
    private String next;
    @JsonProperty("objects")
    private List<MyCustomObject> products;

    @JsonSetter("pagination")
    public void deserializePaginationNode(Map<String,Object> paginationNode) {
        if (MapUtils.isEmpty(paginationNode)) {
           log.error("Failed to deserialize pagination node: {}", Arrays.toString(paginationNode.entrySet().toArray()));
           return;
        }

        this.count = (Integer) paginationNode.get("count");
        this.totalPages = (Integer) paginationNode.get("totalPages");
        this.totalResources = (Integer) paginationNode.get("totalResources");
        this.next = (String) paginationNode.get("next");
    }

MyCustomObject 中,我将 @JsonIgnore 注释与 @JsonSetter("node name") 结合使用,以强制 Jackson 使用我的方法进行反序列化:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyCustomObject {
    @JsonIgnore
    private List<String> someField;
    @JsonIgnore
    private String anotherField;

    // ...

    @JsonSetter("model")
    public void deserializeModelNode(Map<String,Object> modelNode) {
        if (MapUtils.isEmpty(modelOfferingNode)) {
            log.error("Failed to deserialize model node: {}", Arrays.toString(modelOfferingNode.entrySet().toArray()));
            return;
        }

        this.field = (List<String>) modelNode.get("field");
        this.anotherField = (String) modelOfferingNode.get("anotherField");
    }

这种方法有效,但我很好奇是否有更有效的实现可以实现相同的结果,即使用 Jackson 将具有不同嵌套级别的约 200 个字段的复杂数据结构展平为平面结构。例如,我知道您可以使用 @JsonDeserialize 并编写一个较低级别的反序列化器,它可以处理更原始的数据类型,例如 JsonNode 等。有人知道一个好的替代方案吗?

如果您必须处理大量数据,请考虑使用 Vert.x JSON parser

处理响应缓冲区时,您必须在对象值和事件模式之间切换:这将让您确定是否有下一页,同时使对象数据处理更容易。

Parsing Large JSON Files using Jackson Streaming API Example

Jackson's Streaming API. Jackson is one of the most popular JSON processing framework and provides three main model to parse and process JSON data including Streaming API, data binding and tree model. Out of these three, Streaming works at lowest level and can be used to parse huge JSON response upto even giga bytes of size