过滤具有相同 ID 和给定条件的对象的 json 数组

Posted

技术标签:

【中文标题】过滤具有相同 ID 和给定条件的对象的 json 数组【英文标题】:filter a json array of objects with same ID and given condition 【发布时间】:2022-01-18 08:56:25 【问题描述】:

给定一个像这样的 Json 数组:

["TID":"12P","APP":"V", "TID":"12P","APP":"S",
"TID":"12P","APP":"V_xz", "TID":"12P","APP":"V_tvc",
"TID":"78L","APP":"V", "TID":"78L","APP":"V_tvc",
"TID":"7MP","APP":"S", "TID":"7MP","APP":"V_tvc",
"TID":"5P","APP":"V_xv", "TID":"5P","APP":"V_cd"]

上述Json Array的最终输出应该是:

["TID":"12P","APP":"V",
"TID":"78L","APP":"V",
"TID":"7MP","APP":"S"]

对于每条具有相同 TID 的记录,我必须检查对象是否:

    让 APP 同时存在“V”和“S”,然后只获取 APP="V" 的对象 将 APP 设为“V”而不是“S”,则应获取 APP="V" 的对象 将 APP 设为“S”而不是“V”,则应获取 APP="S" 的对象 应忽略 APP 作为“V”和“S”以外的值

【问题讨论】:

请分享您解决此问题的尝试...练习? 【参考方案1】:

解决方案可能会因使用的JsonArray 实现而异,但一般方法包括以下任务:

    将输入的JSON字符串读入JsonArray 迭代输入数组,过滤掉包含"APP"字段且值不是"V""S"的值 按"TID" 字段分组,解决冲突以支持"V" 值 添加到结果JsonArray

如果使用javax.json.JsonArray,解决方案代码如下:

static JsonArray filterArray(String json) 
    JsonReader reader = Json.createReader(new StringReader(json));
    JsonArray jsonaArray = reader.readArray();

    Set<String> appValues = new HashSet<>(Arrays.asList("V", "S"));

    return jsonaArray.stream()
            .map(JsonObject.class::cast)
            .filter(jo -> appValues.contains(jo.getString("APP")))
            .collect(Collectors.toMap(
                jo -> jo.getString("TID"),
                jo -> jo,
                (v1, v2) -> Stream.of(v1, v2)
                    .sorted(Comparator.comparing((JsonObject job) -> job.getString("APP"))
                                      .reversed()
                    )
                    .findFirst().get(),
                    LinkedHashMap::new
            ))
            .values()
            .stream()
            .collect(JsonCollectors.toJsonArray());

这里使用javax.json-api库提供JsonCollectors

测试:

String JSON = ...; // long JSON data
System.out.println(filterArray(JSON));

输出:

["TID":"12P","APP":"V","TID":"78L","APP":"V","TID":"7MP","APP":"S"]

使用 Jackson 库的类似解决方案:

static ArrayNode filterArrayJackson(String json) throws IOException 
    Set<String> appValues = new HashSet<>(Arrays.asList("V", "S"));
    ObjectMapper mapper = new ObjectMapper();
    ArrayNode array = (ArrayNode) mapper.readTree(new StringReader(json));
    return StreamSupport.stream(array.spliterator(), false)
            .filter(node -> appValues.contains(node.get("APP").asText()))
            .collect(Collectors.toMap(
                node -> node.get("TID").asText(),
                node -> node,
                (v1, v2) -> Stream.of(v1, v2)
                    .sorted(Comparator.comparing((JsonNode job) -> job.get("APP").asText())
                                      .reversed()
                    )
                    .findFirst().get(),
                    LinkedHashMap::new
            ))
            .values()
            .stream()
            .reduce(
                mapper.createArrayNode(), 
                ArrayNode::add, 
                (acc, arr) -> acc.addAll(arr); return acc;
            );

可以使用自定义收集器代替Stream::reduce

// ...
    .collect(Collector.of(
        mapper::createArrayNode, ArrayNode::add, 
        (acc, arr) -> acc.addAll(arr); return acc;
    ));

【讨论】:

以上是关于过滤具有相同 ID 和给定条件的对象的 json 数组的主要内容,如果未能解决你的问题,请参考以下文章

无法使用杰克逊反序列化包含 2 个具有相同 ID 的对象的 Json

根据时间戳条件过滤数组中的唯一对象

以 JSON 或 SQLITE (Android) 存储数据

如何使用 jq 按元素属性值过滤对象数组?

选择给定条件的行

[对象值使用JSON解析显示具有相同名称的对象和字符串