如何基于公共键高效合并两个JSON字符串?
基于公共键合并两个JSON数组的高效Jackson实现方案
问题背景
现有两个JSON数组字符串,结构如下:
JSON-1:
[ { "title": "ABC", "edition": 7, "year": 2011 }, { "title": "DEF", "edition": 2, "year": 2012 }, { "title": "XYZ", "edition": 3, "year": 2013 } ]
JSON-2:
[ { "title": "ABC", "price": "20" }, { "title": "DEF", "price": "20" }, { "title": "XYZ", "price": "20" } ]
需基于公共键title合并两个数组,将JSON-2的属性合并到JSON-1中,最终得到包含所有属性的JSON数组,要求:
- 不使用实体/模型类
- 禁止使用Gson库
- 以最少循环和对象创建实现
尝试过两种Jackson方案但遇到问题:
- 使用
List<JSONObject> listObj = objectMapper.readValue(jsonOneString, new TypeReference<List<JSONObject>>(){});时触发未知属性异常 - 使用
JsonNode node = objectMapper.readTree(jsonOneString);但无法继续完成合并操作
已写出title匹配的判断逻辑:
if (json1elem.get("title")!=null && json2elem.get("title")!=null && json1elem.get("title").equals(json2elem.get("title"))) { // 合并逻辑待实现 }
高效实现方案
核心思路是先将第二个JSON数组转换为title到对应节点的映射表,再遍历第一个数组直接通过映射表匹配合并,避免嵌套循环,将时间复杂度从O(n*m)优化为O(n+m),同时使用Jackson原生的可变节点ObjectNode完成属性合并。
完整代码实现
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class JsonArrayMerge { public static void main(String[] args) throws Exception { // 输入的两个JSON字符串 String jsonStr1 = "[{\"title\":\"ABC\",\"edition\":7,\"year\":2011},{\"title\":\"DEF\",\"edition\":2,\"year\":2012},{\"title\":\"XYZ\",\"edition\":3,\"year\":2013}]"; String jsonStr2 = "[{\"title\":\"ABC\",\"price\":\"20\"},{\"title\":\"DEF\",\"price\":\"20\"},{\"title\":\"XYZ\",\"price\":\"20\"}]"; ObjectMapper objectMapper = new ObjectMapper(); // 读取两个JSON数组为JsonNode JsonNode arrayNode1 = objectMapper.readTree(jsonStr1); JsonNode arrayNode2 = objectMapper.readTree(jsonStr2); // 构建title到JsonNode的映射表,仅需遍历一次JSON-2 Map<String, JsonNode> titleNodeMap = new HashMap<>(); for (JsonNode node : arrayNode2) { String title = node.get("title").asText(); titleNodeMap.put(title, node); } // 遍历JSON-1的节点,合并对应属性 Iterator<JsonNode> iterator = arrayNode1.iterator(); while (iterator.hasNext()) { JsonNode currentNode = iterator.next(); if (currentNode instanceof ObjectNode) { ObjectNode mutableNode = (ObjectNode) currentNode; String title = currentNode.get("title").asText(); // 从映射表中获取对应title的JSON-2节点 JsonNode mergeNode = titleNodeMap.get(title); if (mergeNode != null) { // 合并除title外的所有属性 mergeNode.fields().forEachRemaining(entry -> { if (!"title".equals(entry.getKey())) { mutableNode.set(entry.getKey(), entry.getValue()); } }); } } } // 输出格式化后的合并结果 String mergedJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(arrayNode1); System.out.println(mergedJson); } }
方案说明
- 映射表构建:将JSON-2的节点以
title为key存入HashMap,后续匹配时直接O(1)查找,避免嵌套循环的低效。 - 可变节点操作:Jackson的
JsonNode是不可变的,因此需要将其转为ObjectNode才能修改节点属性,完成合并。 - 属性合并逻辑:遍历JSON-2节点的所有字段,跳过重复的
title键,将其余属性添加到JSON-1的对应节点中。
解决之前的问题
- 针对第一种方案的未知属性异常:若坚持使用
List<ObjectNode>,可添加配置objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)关闭未知属性校验,但使用readTree更简洁直接。 - 针对第二种方案无法推进:核心是没有将不可变的
JsonNode转为ObjectNode,也未使用映射表优化匹配效率。
内容的提问来源于stack exchange,提问作者New2Java
相关产品推荐
相关产品推荐

