【问题标题】:How to Parse Nested Value Into List From JSON Array Using Jackson如何使用 Jackson 将 JSON 数组中的嵌套值解析为列表
【发布时间】:2019-09-12 20:47:40
【问题描述】:

所以,我四处寻找这个答案,但我还没有找到与我的情况相符的解决方案。基本上,我的 JSON 结构如下所示:

{
    "count": 4424,
    "results": [
        {"id": 2718, "name": "fox", "location": "Omaha"}, 
        {"id": 2719, "name": "bear", "location": "Miami"}
        ...
        more objects containing an ID field
        ...
    ]
}

我希望能够解析出所有“id”值并将它们存储在一个列表中,我将循环该列表以对其他端点进行后续的 REST 调用。如果我想要包含 id 字段的整个对象,我知道我可以创建一个新对象并创建一个自定义 JSON 反序列化器,但由于它只是我想要的一个字段,所以感觉有点矫枉过正。

我最初的想法是在自定义反序列化器中做一些事情,如下所示:

List<String> idList = new ArrayList<String>();
JsonNode node = jp.getCodec().readTree(jp); // jp = JsonParser
JsonNode resultsNode = node.get("results");
resultsNode.forEach(result -> {
    JsonNode idNode = result.get("id");
    if (idNode != null) {
        String id = idNode.textValue().toUpperCase();
        idList.add(id);
    }
});

这是处理这个问题的正确方法吗?如果没有,将这些 id 值解析为列表的最佳/有效方法是什么?

另外,我在网上阅读到可能想要在尝试这样的事情时进行缓存(列表可能包含 1000 多个 ID),但我不太确定在这种情况下这意味着什么。谁能解释一下?

【问题讨论】:

    标签: java json list parsing jackson


    【解决方案1】:

    如果您只需要JSON 有效负载的一部分,您可以使用JsonPath 库。您可以在下面找到如何使用它的示例:

    import com.jayway.jsonpath.JsonPath;
    
    import java.io.File;
    import java.util.List;
    
    public class JsonPathApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            List<String> ids = JsonPath.read(jsonFile, "$.results.[*].id");
            System.out.println(ids);
        }
    }
    

    上面的代码打印:

    [2718,2719]
    

    使用Jackson,您可以根据需要创建尽可能小的POJO 模型:

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.io.File;
    import java.util.List;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    
            List<Id> ids = mapper.readValue(jsonFile, Response.class).getIds();
            System.out.println(ids);
        }
    }
    
    class Response {
    
        private final List<Id> ids;
    
        @JsonCreator
        public Response(@JsonProperty("results") List<Id> ids) {
            this.ids = ids;
        }
    
        public List<Id> getIds() {
            return ids;
        }
    }
    
    class Id {
    
        private final String id;
    
        @JsonCreator
        public Id(@JsonProperty("id") String id) {
            this.id = id;
        }
    
        public String getId() {
            return id;
        }
    
        @Override
        public String toString() {
            return id;
        }
    }
    

    上面的代码打印:

    [2718, 2719]
    

    如果我们不想映射所有属性,则需要FAIL_ON_UNKNOWN_PROPERTIES 功能。它应该不会比您使用自定义反序列化器的解决方案慢很多。

    1000+JSON objects 列表中的数字不是很大,应该不是问题。您可以尝试为每个解决方案编写基准并比较结果。

    【讨论】:

      【解决方案2】:

      在没有尝试过的情况下,这看起来像是一个 JSON 文件,它来自于序列化 Sightings-object 之类的东西

      public AnimalSighting {
          public int id;
          public String name;
          public String location;
      }
      public Sightings {
          public int count;
          public ArrayList<AnimalSighting> results;
      }
      

      然后使用new ObjectMapper().readValue(file, Sightings.class); 进行反序列化会很简单

      当然,这些类可以使用带有 getter 和 setter 的私有属性,然后可能需要一些注释(@JsonCreator/@JsonProperty/@JsonGetter/@JsonSetter,具体取决于您在做什么),但一般原则仍然存在。

      【讨论】:

      • 所以我最初的想法是做这样的事情,但就必须为 JSON 数组中的每个项目创建一个新对象而言,这不是矫枉过正,特别是如果我不去使用除 id 之外的任何其他属性?解析字符串值并将其存储在列表中不是更好的性能吗?
      • 最后,您将拥有一个 Sightings-object,其中包含 AnimalSighting-objects 列表。我看不出这会比拥有一个字符串列表更“复杂”。或者我可能误解了你在说什么字符串值? id 可以是字符串而不是示例中所示的整数吗?
      • @Pr0pagate A String 不是Object
      • 如果关注的是内存管理,在需要时将数据卸载到数据库中似乎比将数据的转换字符串表示形式保存在内存中更好。
      • 我刚刚测试了我的建议:反序列化包含 1000 万个条目的示例 JSON 文件大约需要 10 秒(因此该文件的大小约为 1GB)。因此,如果您“仅”有 1000 多个条目,则性能应该不是太大的问题。
      猜你喜欢
      • 2015-09-12
      • 1970-01-01
      • 1970-01-01
      • 2019-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      相关资源
      最近更新 更多