Java中JSONassert,JsonUnit和Jackson三方比对

 更新时间:2026年07月23日 09:51:14   作者:Mcband  
本文主要介绍了Java中JSONassert,JsonUnit和Jackson三方比对,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在工作中遇到需要对前后两组数据进行比对,并返回比对结果,最终决定使用json进行比对,下面是几种方案。

核心工具对比

1. JSONassert(测试场景首选)

JSONassert 是专为测试设计的 JSON 比对工具,支持 忽略字段顺序、数组顺序、数值精度 等宽松模式,差异会以异常形式抛出,包含具体路径和值的对比。

步骤 1:引入依赖(Maven)

<dependency>
    <groupId>org.skyscreamer</groupId>
    <artifactId>jsonassert</artifactId>
    <version>1.5.1</version>
    <scope>test</scope> <!-- 测试场景建议test scope -->
</dependency>

步骤 2:实操示例(比对差异并输出)

import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;

public class JsonAssertDemo {
    public static void main(String[] args) {
        // 待比对的两个JSON字符串(结构相同,值有差异)
        String expectedJson = "{\"name\":\"张三\",\"age\":20,\"address\":{\"city\":\"北京\"},\"hobbies\":[\"篮球\",\"游泳\"]}";
        String actualJson = "{\"name\":\"李四\",\"age\":20,\"address\":{\"city\":\"上海\"},\"hobbies\":[\"篮球\",\"跑步\"]}";

        try {
            // 严格模式(字段顺序、数组顺序、值完全一致才通过)
            JSONAssert.assertEquals(expectedJson, actualJson, JSONCompareMode.STRICT);
        } catch (AssertionError e) {
            // 捕获差异并输出
            System.out.println("JSON差异:\n" + e.getMessage());
        }
    }
}

输出结果(精准定位差异)

JSON差异:
Expected: "张三"
     got: "李四"
at path $["name"]
Expected: "北京"
     got: "上海"
at path $["address"]["city"]
Expected: "游泳"
     got: "跑步"
at path $["hobbies"][1]

关键配置(JSONCompareMode)

  • STRICT:严格模式(字段顺序、数组顺序、值必须完全一致)
  • LENIENT:宽松模式(忽略字段顺序,数组顺序仍校验)
  • NON_EXTENSIBLE:不允许实际 JSON 包含预期外的字段
  • IGNORE_ARRAY_ORDER:忽略数组元素顺序
  • IGNORE_VALUES:只校验结构,忽略值的差异

2. JsonUnit(更友好的测试 API)

JsonUnit 基于 JSONassert 和 Jackson 封装,API 更简洁,差异输出更易读,支持 assertThat 风格的断言(符合 BDD 测试习惯)。

步骤 1:引入依赖

<dependency>
    <groupId>net.javacrumbs.json-unit</groupId>
    <artifactId>json-unit-core</artifactId>
    <version>2.38.0</version>
    <scope>test</scope>
</dependency>
<!-- 依赖Jackson(JsonUnit底层用Jackson解析) -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

步骤 2:实操示例

import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;

public class JsonUnitDemo {
    public static void main(String[] args) {
        String expectedJson = "{\"name\":\"张三\",\"age\":20,\"address\":{\"city\":\"北京\"}}";
        String actualJson = "{\"name\":\"李四\",\"age\":21,\"address\":{\"city\":\"北京\"}}";

        // 比对并输出差异(AssertJ风格)
        assertThatJson(actualJson)
            .isEqualTo(expectedJson)
            .onFailure(failure -> System.out.println("JSON差异:\n" + failure.getMessage()));
    }
}

输出结果

JSON差异:
Expected value <"张三"> but was <"李四"> at path $['name']
Expected value <20> but was <21> at path $['age']

高级用法(忽略指定字段)

// 忽略age字段的差异
assertThatJson(actualJson)
    .whenIgnoringPaths("age")
    .isEqualTo(expectedJson);

3. Jackson(业务开发灵活定制)

Jackson 是 Java 最主流的 JSON 处理库,通过 JsonNode 树模型可完全自定义比对逻辑(适合业务开发中需要精细控制差异的场景)。

步骤 1:引入依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

步骤 2:自定义比对工具类(输出差异路径和值)

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
public class JacksonJsonDiff {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final List<String> DIFFS = new ArrayList<>();
    // 比对两个JSON字符串,返回差异列表
    public static List<String> compareJson(String expectedJson, String actualJson) throws Exception {
        DIFFS.clear();
        JsonNode expectedNode = OBJECT_MAPPER.readTree(expectedJson);
        JsonNode actualNode = OBJECT_MAPPER.readTree(actualJson);
        compareNodes("", expectedNode, actualNode);
        return DIFFS;
    }
    // 递归比对JsonNode节点
    private static void compareNodes(String path, JsonNode expected, JsonNode actual) {
        // 节点类型不同
        if (expected.getNodeType() != actual.getNodeType()) {
            DIFFS.add(path + ":类型不一致(预期:" + expected.getNodeType() + ",实际:" + actual.getNodeType() + ")");
            return;
        }
        // 叶子节点(值节点)
        if (expected.isValueNode()) {
            if (!expected.equals(actual)) {
                DIFFS.add(path + ":值不一致(预期:" + expected.asText() + ",实际:" + actual.asText() + ")");
            }
            return;
        }
        // 对象节点(递归比对子字段)
        if (expected.isObject()) {
            expected.fieldNames().forEachRemaining(fieldName -> {
                String newPath = path.isEmpty() ? "$." + fieldName : path + "." + fieldName;
                JsonNode expectedChild = expected.get(fieldName);
                JsonNode actualChild = actual.get(fieldName);
                if (actualChild == null) {
                    DIFFS.add(newPath + ":实际JSON缺失该字段");
                } else {
                    compareNodes(newPath, expectedChild, actualChild);
                }
            });
            // 检查实际JSON是否有预期外的字段
            actual.fieldNames().forEachRemaining(fieldName -> {
                if (!expected.has(fieldName)) {
                    String newPath = path.isEmpty() ? "$." + fieldName : path + "." + fieldName;
                    DIFFS.add(newPath + ":实际JSON包含预期外的字段");
                }
            });
        }
        // 数组节点(递归比对数组元素)
        if (expected.isArray()) {
            int expectedSize = expected.size();
            int actualSize = actual.size();
            if (expectedSize != actualSize) {
                DIFFS.add(path + ":数组长度不一致(预期:" + expectedSize + ",实际:" + actualSize + ")");
            }
            // 比对数组元素(按索引)
            int maxSize = Math.max(expectedSize, actualSize);
            for (int i = 0; i < maxSize; i++) {
                String newPath = path + "[" + i + "]";
                JsonNode expectedChild = i < expectedSize ? expected.get(i) : null;
                JsonNode actualChild = i < actualSize ? actual.get(i) : null;
                if (expectedChild == null) {
                    DIFFS.add(newPath + ":实际数组多出元素");
                } else if (actualChild == null) {
                    DIFFS.add(newPath + ":实际数组缺失元素");
                } else {
                    compareNodes(newPath, expectedChild, actualChild);
                }
            }
        }
    }
    // 测试
    public static void main(String[] args) throws Exception {
        String expectedJson = "{\"name\":\"张三\",\"age\":20,\"address\":{\"city\":\"北京\"},\"hobbies\":[\"篮球\",\"游泳\"]}";
        String actualJson = "{\"name\":\"李四\",\"age\":20,\"address\":{\"city\":\"上海\"},\"hobbies\":[\"篮球\",\"跑步\"]}";
        List<String> diffs = compareJson(expectedJson, actualJson);
        System.out.println("JSON差异列表:");
        diffs.forEach(diff -> System.out.println("- " + diff));
    }
}

输出结果

JSON差异列表:
- $.name:值不一致(预期:张三,实际:李四)
- $.address.city:值不一致(预期:北京,实际:上海)
- $.hobbies[1]:值不一致(预期:游泳,实际:跑步)

4. Diffuse(轻量级结构化差异输出)

Diffuse 是轻量级工具,可输出 JSON 格式的结构化差异报告(包含添加 / 修改 / 删除的节点),适合需要将差异持久化或传输的场景。

步骤 1:引入依赖

<dependency>
    <groupId>me.snov</groupId>
    <artifactId>diffuse</artifactId>
    <version>0.2.0</version>
</dependency>

步骤 2:实操示例

import me.snov.diffuse.Diffuse;
import me.snov.diffuse.JsonDiff;

public class DiffuseDemo {
    public static void main(String[] args) {
        String expectedJson = "{\"name\":\"张三\",\"age\":20}";
        String actualJson = "{\"name\":\"李四\",\"age\":21,\"gender\":\"男\"}";

        // 生成结构化差异报告
        JsonDiff diff = Diffuse.diff(expectedJson, actualJson);
        System.out.println("结构化差异报告:\n" + diff.toJson());
    }
}

输出结果(JSON 格式)

{
  "changed": [
    {
      "path": "/name",
      "from": "张三",
      "to": "李四"
    },
    {
      "path": "/age",
      "from": 20,
      "to": 21
    }
  ],
  "added": [
    {
      "path": "/gender",
      "value": "男"
    }
  ],
  "removed": []
}

选型建议

  1. 测试场景:优先选 JsonUnit(API 友好)或 JSONassert(轻量);
  2. 业务开发:优先选 Jackson(灵活自定义比对规则);
  3. 需要结构化差异输出:选 Diffuse;
  4. 特殊需求(如忽略数值精度、时间格式):基于 Jackson 扩展自定义比对逻辑。

到此这篇关于Java中JSONassert,JsonUnit和Jackson三方比对的文章就介绍到这了,更多相关Java JSONassert,JsonUnit和Jackson内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Java堆外内存之突破JVM枷锁

    浅谈Java堆外内存之突破JVM枷锁

    这篇文章主要介绍了浅谈Java堆外内存之突破JVM枷锁,涉及jvm内存分配,jvm垃圾回收,堆外内存的垃圾回收等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Java实现多线程文件下载的代码示例

    Java实现多线程文件下载的代码示例

    本篇文章主要介绍了Java实现多线程下载的代码示例,Java多线程可以充分利用CPU的资源,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-02-02
  • Mybatis之Mapper动态代理实例解析

    Mybatis之Mapper动态代理实例解析

    这篇文章主要介绍了Mybatis之Mapper动态代理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Java Stream 的 flatMap 与 map 的核心区别从原理到实战应用全解析

    Java Stream 的 flatMap 与 map 的核

    map进行元素到元素的单层转换,flatMap则将元素映射为流后再扁平化处理,适用于嵌套结构展开,二者核心差异在于是否展开多层数据,选择时需根据数据结构层级和性能需求决定,本文给大家介绍Java Stream 的flatMap与map的核心区别,感兴趣的朋友一起看看吧
    2025-08-08
  • Java中获取当前路径的几种方法总结

    Java中获取当前路径的几种方法总结

    这篇文章主要介绍了Java中获取当前路径的几种方法总结的相关资料,需要的朋友可以参考下
    2017-02-02
  • Java中Velocity快速对变量中的引号特殊字符进行转义

    Java中Velocity快速对变量中的引号特殊字符进行转义

    Velocity是一个基于Java的模板引擎,与Freemarker类似,这篇文章主要介绍了Java中Velocity如何对变量中的引号特殊字符进行转义,主要记录一下在使用中碰到的要对引号特殊字符进行转义的问题,需要的朋友可以参考下
    2023-07-07
  • SpringBoot+Vue.js实现前后端分离的文件上传功能

    SpringBoot+Vue.js实现前后端分离的文件上传功能

    这篇文章主要介绍了SpringBoot+Vue.js实现前后端分离的文件上传功能,需要的朋友可以参考下
    2018-06-06
  • Springboot连接和操作mongoDB方式

    Springboot连接和操作mongoDB方式

    这篇文章主要介绍了Springboot连接和操作mongoDB方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Java实现FTP服务器功能实例代码

    Java实现FTP服务器功能实例代码

    FTP(File Transfer Protocol 文件传输协议)是Internet 上用来传送文件的协议,本文给大家分享Java实现FTP服务器功能实例代码,对java实现ftp服务器相关知识感兴趣的朋友一起学习吧
    2015-12-12
  • java数组算法例题代码详解(冒泡排序,选择排序,找最大值、最小值,添加、删除元素等)

    java数组算法例题代码详解(冒泡排序,选择排序,找最大值、最小值,添加、删除元素等)

    这篇文章主要介绍了java数组算法例题代码详解(冒泡排序,选择排序,找最大值、最小值,添加、删除元素等),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-05-05

最新评论