使用java实现复杂结构多层级的json diff
主要思路:遍历json,根据key排序,判断json element类型,如果是jsonArray的话,转成list并排序,并循环验证list中的每个json element是否相等
package service.utils;
import com.google.gson.*;
import org.slf4j.*;
import org.testng.Assert;
import java.util.*;
public class JsonDiffUtil {
private static final JsonParser parser = new JsonParser();
private static final Gson gson = new Gson();
//默认是忽略值比较
private boolean diffValue = false;
private static final Logger logger = LoggerFactory.getLogger(JsonDiffUtil.class);
public JsonDiffUtil(){};
public JsonDiffUtil(boolean diffValue){
this.diffValue = diffValue;
}
/*
* 验证两个json字符串结构是否一致,正反两次比较
*/
public void diff(String exceptJson,String actualJson){
Assert.assertTrue(equals(exceptJson, actualJson));
Assert.assertTrue(equals(actualJson, exceptJson));
}
/**
* 比较两个json字符串是否
*/
public boolean equals(String exceptJson, String actualJson) {
if(exceptJson.equals(actualJson)){
return true;
}
//json String 转 JsonElement
JsonElement exceptJsonElement = parser.parse(exceptJson);
JsonElement actualJsonElement = parser.parse(actualJson);
//按key排序
sort(exceptJsonElement);
sort(actualJsonElement);
return equals(exceptJsonElement, actualJsonElement);
}
private boolean equals(JsonElement except, JsonElement actual) {
if (except.isJsonObject()) {
return equals(except.getAsJsonObject(), actual.getAsJsonObject());
} else if (except.isJsonArray() ) {
return equals(except.getAsJsonArray(), actual.getAsJsonArray());
} else if (except.isJsonPrimitive()) {
return equals(except.getAsJsonPrimitive(), actual.getAsJsonPrimitive());
} else {
logger.info("except= {},actual= {}",except,actual);
return false;
}
}
private boolean equals(JsonObject except, JsonObject actual) {
Set exceptSet = except.keySet();
for (String key : exceptSet) {
JsonElement exceptValue = except.get(key);
JsonElement actualValue = actual.get(key);
if(actualValue == null){
logger.error("not found key:"+key);
return false;
}
if (exceptValue.isJsonArray() && exceptValue.getAsJsonArray().size() != actualValue.getAsJsonArray().size()) {
logger.error("JSON Array size not equal,key is {}",key);
return false;
}
boolean flag = equals(exceptValue, actualValue);
if(!flag){
if(exceptValue.isJsonPrimitive()){
if (diffValue) {
logger.error("value not equal,key is {}, except value is {}, actual value is {}",key,exceptValue,actualValue);
}
else {
logger.error("not found key:"+key);
}
}
return false;
}
}
return true;
}
private boolean equals(JsonArray exceptArray, JsonArray actualArray) {
List exceptList = toSortedList(exceptArray);
List actaulList = toSortedList(actualArray);
for (int i = 0; i < exceptList.size(); i++) {
if (!equals(exceptList.get(i), actaulList.get(i))) {
return false;
}
}
return true;
}
private boolean equals(JsonPrimitive except, JsonPrimitive actual) {
if(!diffValue){
return actual != null;
}
return except.equals(actual);
}
private List toSortedList(JsonArray array) {
List list = new ArrayList<>();
array.forEach(list::add);
list.sort(Comparator.comparing(gson::toJson));
return list;
}
/*
* 根据json key排序
*/
public static void sort(JsonElement je) {
if (je.isJsonNull()) {
return;
}
if (je.isJsonPrimitive()) {
return;
}
if (je.isJsonArray()) {
JsonArray array = je.getAsJsonArray();
Iterator it = array.iterator();
while (it.hasNext()) {
sort(it.next());
}
return;
}
if (je.isJsonObject()) {
Map tm = new TreeMap(getComparator());
for (Map.Entry en : je.getAsJsonObject().entrySet()) {
tm.put(en.getKey(), en.getValue());
}
for (Map.Entry en : tm.entrySet()) {
je.getAsJsonObject().remove(en.getKey());
je.getAsJsonObject().add(en.getKey(), en.getValue());
sort(en.getValue());
}
return;
}
}
private static Comparator getComparator() {
return String::compareTo;
}
}
用到的依赖包
org.testng
testng
6.9.8
com.google.code.gson
gson
2.8.5
org.slf4j
slf4j-log4j12
1.7.25