You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:32:11