JSON报文差异化对比
- 方式一,使用jsonassert
- 方式二,使用json-patch
有这么一组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
使用此方法的好处是可以自定义一些对比规则(强烈推荐)
- 首先引入pom依赖
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.1</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) {
//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);
}
}
}
- 对比效果
两者相等
person.addresses[addrId=1].addrType
Expected: 103
got: 103
两者相等
- 上述自定义的过滤规则
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: []