如何在Java中比较两个随机JSON字符串并返回差异(忽略对象顺序)
如何在Java中比较两个JSON字符串并返回差异Map(忽略键值对顺序)
嘿,这个需求我之前也碰到过!要在Java里对比两个JSON字符串,还得忽略键的顺序,最后把差异以Map形式返回,其实核心思路就是把JSON转成不依赖插入顺序的结构化数据,再递归对比差异。下面我给你一步步讲清楚实现方式:
1. 先搞定JSON解析依赖
首先得选个靠谱的JSON解析库,Jackson是个不错的选择,灵活还好配置。如果用Maven的话,先把依赖加到你的pom.xml里:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> <!-- 建议用最新稳定版 --> </dependency>
2. 把JSON解析成有序Map(忽略原顺序)
要忽略JSON里键的顺序,我们得让Jackson把JSON对象解析成TreeMap——因为TreeMap会自动按键排序,这样不管原JSON里键的顺序怎么乱,解析后的Map结构都是一致的,对比起来就不会因为顺序问题误判差异了。
写个工具方法来做解析:
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.TreeMap; public class JsonDiffUtils { private static final ObjectMapper objectMapper; static { objectMapper = new ObjectMapper(); // 配置Jackson默认用TreeMap解析JSON对象,保证键有序 objectMapper.setDefaultSetterInfo(objectMapper.getDefaultSetterInfo().withMapType(TreeMap.class)); } // 将JSON字符串解析为Map<String, Object> public static Map<String, Object> parseJsonToMap(String json) throws Exception { return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); } }
3. 递归对比两个Map,收集差异
接下来就是核心的对比逻辑了,我们递归遍历两个Map的所有键,把三种差异都记录下来:
- 只在第一个JSON里存在的键
- 只在第二个JSON里存在的键
- 键存在但值不同的情况
差异的键用点分隔的路径表示(比如address.city),值用一个包含新旧值的Map来存储:
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class JsonDiffUtils { // ... 上面的解析代码 ... // 对外暴露的方法:比较两个JSON字符串,返回差异Map public static Map<String, Object> compareJsonStrings(String json1, String json2) throws Exception { Map<String, Object> map1 = parseJsonToMap(json1); Map<String, Object> map2 = parseJsonToMap(json2); Map<String, Object> differences = new HashMap<>(); compareMaps(map1, map2, "", differences); return differences; } // 递归对比两个Map的私有方法 private static void compareMaps(Map<String, Object> map1, Map<String, Object> map2, String currentPath, Map<String, Object> differences) { // 获取所有键的并集,避免漏掉只在其中一个Map里存在的键 Set<String> allKeys = new HashSet<>(); allKeys.addAll(map1.keySet()); allKeys.addAll(map2.keySet()); for (String key : allKeys) { // 拼接当前路径,比如从""到"name",再到"address.city" String newPath = currentPath.isEmpty() ? key : currentPath + "." + key; Object value1 = map1.get(key); Object value2 = map2.get(key); if (value1 == null && value2 == null) { continue; // 两个都不存在,跳过 } else if (value1 == null) { // 只在第二个JSON里存在的键 differences.put(newPath, Map.of("newValue", value2)); } else if (value2 == null) { // 只在第一个JSON里存在的键 differences.put(newPath, Map.of("oldValue", value1)); } else if (value1 instanceof Map && value2 instanceof Map) { // 嵌套Map,递归对比 compareMaps((Map<String, Object>) value1, (Map<String, Object>) value2, newPath, differences); } else if (!value1.equals(value2)) { // 值不同的情况 differences.put(newPath, Map.of("oldValue", value1, "newValue", value2)); } } } }
4. 测试一下效果
写个主方法测试看看,比如这两个JSON,键的顺序不一样,但我们的方法能正确识别差异:
public class Main { public static void main(String[] args) throws Exception { String json1 = "{\"name\":\"Alice\",\"age\":30,\"address\":{\"city\":\"New York\",\"zip\":\"10001\"}}"; String json2 = "{\"age\":31,\"name\":\"Alice\",\"address\":{\"zip\":\"10002\",\"country\":\"USA\"}}"; Map<String, Object> diffResult = JsonDiffUtils.compareJsonStrings(json1, json2); System.out.println("差异结果:"); diffResult.forEach((path, detail) -> System.out.println(path + ": " + detail)); } }
运行后输出会是:
age: {oldValue=30, newValue=31} address.city: {oldValue=New York} address.zip: {oldValue=10001, newValue=10002} address.country: {newValue=USA}
5. 一些注意事项
- 数组处理:上面的代码只处理了JSON对象(Map),如果你的JSON包含数组,需要额外添加数组对比的逻辑——比如逐个元素对比,或者如果数组是无序的,转成集合后对比。
- 类型一致性:如果同一个键在两个JSON中的类型不同(比如一个是字符串"30",一个是数字30),
equals会返回false,会被识别为差异,这符合预期。 - 性能优化:对于超大JSON,递归对比可能有性能问题,可以考虑用迭代代替递归,或者使用专门的JSON差异库(比如
com.github.java-json-tools:json-patch)来简化开发。
内容的提问来源于stack exchange,提问作者iftekhar iftekhar
相关产品推荐
相关产品推荐

