java对JSON字符串差异化对比分析

 更新时间:2026年03月07日 15:24:39   作者:祁仙森  
文章介绍了两种对比Java中JSON字符串差异的方法:使用Jackson包通过自定义过滤规则和使用json-patch库,前者可以自定义对比规则,而后者具有更好的扩展性,文中提供了实操示例并总结了经验

java JSON字符串差异化对比

需求是要对比json,相同可以不展示,不同需指明差异字段

前提引入jackson包

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.3</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.13.3</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>
</dependencies>

方式一,使用jsonassert

使用此方法的好处是可以自定义一些对比规则(强烈推荐)

  1. 首先引入pom依赖
<dependency>
    <groupId>org.skyscreamer</groupId>
    <artifactId>jsonassert</artifactId>
    <version>1.5.1</version>
</dependency>
  1. 编写代码
public class Main {
    //与jsonStr2中orgSeqNo不一样,还有addresses中的addrType为103时的字段类型不一样
    static String jsonStr1 = "{\"orgSeqNo\":\"123123\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":103,\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    static String jsonStr2 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"103\",\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    //与jsonStr2对比,addresses数组中的顺序换了
    static String jsonStr3 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"103\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    //与jsonStr2对比,addresses数组中的103位数字类型
    static String jsonStr4 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":103,\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";



    public static void main(String[] args) {
        //aasdasdbc为过滤的节点,随意指定
        Customization[] customizations = {new Customization("aasdasdbc",(o1, o2) -> { return true;})};
        JSONCompareResult result = null;
        JSONCompareResult result1 = null;
        JSONCompareResult result2 = null;
        try {
            // JSONCompareMode.NON_EXTENSIBLE  不可扩展模式,可忽略数组排序
            // 宽大模式(LENIENT) 可扩展,数组不用排序
            // 严格模式(STRICT)  不可扩展, 数组严格排序
            // 严格排序模式(STRICT_ORDER)  如字面意思,可扩展
			// 扩展的意思是已有的[1,23,45]和[1,23,45,6]在STRICT_ORDER是可以的

            // 使用自定义比较规则
            result = JSONCompare.compareJSON(jsonStr1, jsonStr2, new MyCustomComparator(JSONCompareMode.NON_EXTENSIBLE, customizations));
            // 过滤orgSeqNo根节点字段
            result1  = JSONCompare.compareJSON(jsonStr1, jsonStr3, new CustomComparator(JSONCompareMode.NON_EXTENSIBLE,
                    Customization.customization("orgSeqNo", (o1, o2) -> {
                        return true;
                    })));
            // 过滤orgSeqNo根节点,以及过滤person根节点下的addresses数组节点的addrType节点字段
            result2 = JSONCompare.compareJSON(jsonStr1, jsonStr3, new CustomComparator(JSONCompareMode.NON_EXTENSIBLE,
                    Customization.customization("orgSeqNo", (o1, o2) -> { return true;}),
                    Customization.customization("person.addresses[*].addrType", (o1, o2) -> { return true;})));
            if (result.passed()) {
                System.out.println("两者相等");
            } else {
                System.out.println(result.getMessage());
            }
            
            if (result1.passed()) {
                System.out.println("两者相等");
            } else {
                System.out.println(result1.getMessage());
            }
            if (result2.passed()) {
                System.out.println("两者相等");
            } else {
                System.out.println(result2.getMessage());
            }
        } catch (JSONException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
  1. 对比效果
两者相等
person.addresses[addrId=1].addrType
Expected: 103
     got: 103

两者相等
  1. 上述自定义的过滤规则
public class MyCustomComparator extends CustomComparator {
    private final Collection<Customization> customizations;

    public MyCustomComparator(JSONCompareMode mode, Customization[] customizations) {
        super(mode);
        this.customizations = Arrays.asList(customizations);
    }

    //比较器默认调用此方法,如果要实现复杂逻辑,可以重写
    @Override
    public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {
        Customization customization = getCustomization(prefix);
        if (customization != null) {
            try {
                if (!customization.matches(prefix, actualValue, expectedValue, result)) {
                    result.fail(prefix, expectedValue, actualValue);
                }
            } catch (ValueMatcherException e) {
                result.fail(prefix, e);
            }
        } else {
			// 过滤某些节点字段
            if (prefix.indexOf("orgSeqNo") != -1 || prefix.indexOf("addrType") != -1) {
                return;
            }
            // todo 可以设置一些复杂的比较,例如某个字段的数值字符串和数值可忽略,小数和某些整数可忽略

            if ((expectedValue == null && actualValue != null) || (expectedValue != null && actualValue == null)) {
                result.fail(prefix, expectedValue, actualValue);
            }
            if (expectedValue.getClass().isAssignableFrom(actualValue.getClass())) {
                if (expectedValue instanceof JSONArray) {
                    compareJSONArray(prefix, (JSONArray) expectedValue, (JSONArray) actualValue, result);
                } else if (expectedValue instanceof JSONObject) {
                    compareJSON(prefix, (JSONObject) expectedValue, (JSONObject) actualValue, result);
                } else if (!expectedValue.equals(actualValue)) {
                    result.fail(prefix, expectedValue, actualValue);
                }
            } else {
                result.fail(prefix, expectedValue, actualValue);
            }
        }
    }

    private Customization getCustomization(String path) {
        for (Customization c : customizations) {
            if (c.appliesToPath(path)) {
                return c;
            }
        }
        return null;
    }

}

方式二,使用json-patch

可扩展性或者说自定义比较较差

<dependency>
    <groupId>com.github.java-json-tools</groupId>
    <artifactId>json-patch</artifactId>
    <version>1.11</version>
</dependency>
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.4.0</version>
</dependency>

实操举例

public class Main {
    //与jsonStr2中orgSeqNo不一样,还有addresses中的addrType为103时的字段类型不一样
    static String jsonStr1 = "{\"orgSeqNo\":\"123123\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":103,\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    static String jsonStr2 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"103\",\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    //与jsonStr2对比,addresses数组中的顺序换了
    static String jsonStr3 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"103\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";
    //与jsonStr2对比,addresses数组中的103位数字类型
    static String jsonStr4 = "{\"orgSeqNo\":\"12e9fc45c09b4a69b7aa2bbcb1313cba\",\"code\":200,\"person\":{\"partyId\":\"10000000001\",\"partyName\":\"张三\",\"partyCertType\":\"10100\",\"partyCertNo\":\"320925199612122314\",\"partyStatus\":\"1\",\"addresses\":[{\"addrId\":\"1\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":103,\"phone\":\"13213137625\"},{\"addrId\":\"2\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"101\",\"phone\":\"13213137625\"},{\"addrId\":\"3\",\"partyId\":\"10000000001\",\"city\":\"苏州\",\"detailAddr\":\"相城区双塔街道33号\",\"addrType\":\"102\",\"phone\":\"13213137625\"}]},\"message\":\"ok\"}";



    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        DocumentContext existingDoc = JsonPath.parse(jsonStr1);
        DocumentContext incomingDoc = JsonPath.parse(jsonStr4);
        existingDoc.delete("orgSeqNo");
        incomingDoc.delete("orgSeqNo");
        JsonNode existingNode = mapper.valueToTree(existingDoc.jsonString());
        JsonNode incomingNode = mapper.valueToTree(incomingDoc.jsonString());
        String diffs = JsonDiff.asJson(existingNode,incomingNode).toString();
        System.out.println("Difference: " + diffs);
    }
}

比较结果

Difference: []

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • springBoot热部署、请求转发与重定向步骤详解

    springBoot热部署、请求转发与重定向步骤详解

    这篇文章主要介绍了springBoot热部署、请求转发与重定向,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • springboot3.x中Jakarta包无法引入的问题

    springboot3.x中Jakarta包无法引入的问题

    最近想将一些项目升级到springboot3.x和java17的时候,发现项目依赖中有Jakarta的包,但是代码标红提示没有相关的类,本文就来介绍一下解决方法,感兴趣的可以了解一下
    2024-02-02
  • springIOC的使用流程及spring中使用类型转换器的方式

    springIOC的使用流程及spring中使用类型转换器的方式

    Spring IOC是Spring框架的核心原理之一,它是一种软件设计模式,用于管理应用程序中的对象依赖关系,这篇文章主要介绍了springIOC的使用流程以及spring中如何使用类型转换器,需要的朋友可以参考下
    2023-06-06
  • 使用RedisAtomicLong优化性能问题

    使用RedisAtomicLong优化性能问题

    这篇文章主要介绍了使用RedisAtomicLong优化性能问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • java ThreadLocal线程局部变量常用方法使用场景示例详解

    java ThreadLocal线程局部变量常用方法使用场景示例详解

    这篇文章主要介绍了为大家java ThreadLocal线程局部变量常用方法使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • Java中的Set接口实现类HashSet和LinkedHashSet详解

    Java中的Set接口实现类HashSet和LinkedHashSet详解

    这篇文章主要介绍了Java中的Set接口实现类HashSet和LinkedHashSet详解,Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了,需要的朋友可以参考下
    2024-01-01
  • Java身份证验证方法实例详解

    Java身份证验证方法实例详解

    这篇文章主要介绍了Java身份证验证方法实例详解的相关资料,需要的朋友可以参考下
    2017-04-04
  • Spring Boot中常用的参数传递注解示例详解

    Spring Boot中常用的参数传递注解示例详解

    这篇文章主要介绍了Spring Boot中常用的参数传递注解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • 全面了解Java中的内部类和匿名类

    全面了解Java中的内部类和匿名类

    下面小编就为大家带来一篇全面了解Java中的内部类和匿名类。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-07-07
  • SpringMVC通过Ajax处理Json数据的步骤详解

    SpringMVC通过Ajax处理Json数据的步骤详解

    这篇文章主要介绍了SpringMVC通过Ajax处理Json数据的步骤详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04

最新评论