在工作中遇到需要对前后两组数据进行比对,并返回比对结果,最终决定使用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": []
}选型建议
- 测试场景:优先选 jsonunit(api 友好)或 jsonassert(轻量);
- 业务开发:优先选 jackson(灵活自定义比对规则);
- 需要结构化差异输出:选 diffuse;
- 特殊需求(如忽略数值精度、时间格式):基于 jackson 扩展自定义比对逻辑。
到此这篇关于java中jsonassert,jsonunit和jackson三方比对的文章就介绍到这了,更多相关java jsonassert,jsonunit和jackson内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论