Java实现比对json报文(不考虑内部顺序 + 可设置跳过部分字段比对)
1.前言
- 最近在处理一个报文比对的问题,发现两个json报文里如果一个节点有多条数据的话,可能有顺序不一样的情况,导致报文比对程序失效,所以本文主要实现对于json报文中JSONArray顺序不一致时也能对比!
2.排序设计
- 根据实体里的某一个字段(主键最好,不然值相同的话,排序失效)进行排序。
- 核心代码如下:
- 注意:为了测试效果,加了一个标志位,isSort,后续可以将此属性去掉!
3.对比效果
(1)简单报文比对
- 可以看出比对结果没有问题,该比对出来的都比对出来了
(2)复杂报文比对——存在顺序问题
a.比对的报文内容:
- 可看出,dogs里两条数据,但是其实内容是一样的,只不过顺序不一样,我们期望对比结果应该是dogs没有不一样的内容。
b.对比效果
1)不排序的对比效果
- 很明显,这不是我们想要的结果,dogs里的两条数据命名是一致的,却对比出不一致!
2)排序后的对比效果
- 这个才是我们想要的结果!
4.附代码
(1)实体
package com.liu.susu.json.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Set;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PetShop {
private String shopNum;
private String shopName;
private String shopAddress;
private Set<Cat> cats;
private List<Dog> dogs;
}
package com.liu.susu.json.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cat {
private String catNo;
private String catName;
private Integer catAge;
}
package com.liu.susu.json.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
private String dogNo;
private String dogName;
private Integer dogAge;
}
(2)准备测试数据
package com.liu.susu.json;
import com.alibaba.fastjson.JSONObject;
import com.liu.susu.json.entity.Cat;
import com.liu.susu.json.entity.Dog;
import com.liu.susu.json.entity.PetShop;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MakeJsonCompareDatas {
public static String getJsonDataNewStr(){
PetShop petShop = new PetShop();
petShop.setShopNum("s-1001");
petShop.setShopName("素素宠物店");
petShop.setShopAddress("全国各地之天津");
Set<Cat> cats = new HashSet<>();
Cat cat = new Cat();
cat.setCatNo("c-1001");
cat.setCatName("布偶喵喵");
cat.setCatAge(1);
cats.add(cat);
List<Dog> dogs = new ArrayList<>();
Dog dog1 = new Dog();
dog1.setDogNo("d-1001");
dog1.setDogName("麦兜");
dog1.setDogAge(2);
dogs.add(dog1);
Dog dog2 = new Dog();
dog2.setDogNo("d-1002");
dog2.setDogName("贝塔");
dog2.setDogAge(1);
dogs.add(dog2);
petShop.setCats(cats);
petShop.setDogs(dogs);
return JSONObject.toJSONString(petShop);
//return JSONObject.toJSON(petShop).toString();
}
public static String getJsonDataOldStr(){
PetShop petShop = new PetShop();
petShop.setShopNum("s-1001");
petShop.setShopName("素素宠物店");
petShop.setShopAddress("全国各地之天津");
Set<Cat> cats = new HashSet<>();
Cat cat = new Cat();
cat.setCatNo("c-1001");
cat.setCatName("喵喵布偶");
cat.setCatAge(1);
cats.add(cat);
List<Dog> dogs = new ArrayList<>();
Dog dog2 = new Dog();
dog2.setDogNo("d-1002");
dog2.setDogName("贝塔");
dog2.setDogAge(1);
dogs.add(dog2);
Dog dog1 = new Dog();
dog1.setDogNo("d-1001");
dog1.setDogName("麦兜");
dog1.setDogAge(2);
dogs.add(dog1);
petShop.setCats(cats);
petShop.setDogs(dogs);
return JSONObject.toJSONString(petShop);
//return JSONObject.toJSON(petShop).toString();
}
public static void main(String[] args) {
String oldJsonStr = MakeJsonCompareDatas.getJsonDataOldStr();
String newJsonStr = MakeJsonCompareDatas.getJsonDataNewStr();
System.out.println("oldJsonStr===>"+oldJsonStr);
System.out.println("newJsonStr===>"+newJsonStr);
}
}
(3)排序工具类
(4)对比工具类
package com.liu.susu.json;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;
public class JsonCompareUtils {
//标志位:对json报文中含有JsonArray类型的数据是否进行排序
private static boolean isSort;
private Map<String, Object> oldJsonToMap = new LinkedHashMap<>();
private Map<String, Object> newJsonToMap = new LinkedHashMap<>();
//每一个实体里的排序字段
private static Map<String, String> filedNameMap = new HashMap<>();
static {
filedNameMap.put("dogs", "dogNo");
filedNameMap.put("cats", "catNo");
}
//可以跳过比对的字段
private static String[] skipCompareFiledNameMap = {"dogAge", "catAge", "catName"};
/**
* 两json报文比对入口
*
* @param oldJsonStr
* @param newJsonStr
* @return
*/
public String compareTwoJson(String oldJsonStr, String newJsonStr) {
/**
* 递归遍历json对象所有的key-value,以map形式的path:value进行存储
* 然后对两个map进行比较
*/
convertJsonToMap(JSON.parseObject(oldJsonStr), "", false);
convertJsonToMap(JSON.parseObject(newJsonStr), "", true);
//获取比较结果
Map<String, Object> differenceMap = compareTwoMaps(oldJsonToMap, newJsonToMap);
String diffJsonResult = convertMapToJson(differenceMap);
return diffJsonResult;
}
/**
* 将json数据转换为map存储--用于后续比较map
*
* @param json
* @param root
* @param isNew 区别新旧报文
*/
private void convertJsonToMap(Object json, String root, boolean isNew) {
if (json instanceof JSONObject) {
JSONObject jsonObject = ((JSONObject) json);
Iterator iterator = jsonObject.keySet().iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
Object value = jsonObject.get(key);
String newRoot = "".equals(root) ? key + "" : root + "." + key;
fillInResultMap(value, newRoot, isNew);
}
} else if (json instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) json;
//将jsonArray进行排序
if (isSort) {
//需要排序
String sortEntityName = root.substring(root.lastIndexOf(".") + 1);
String sortFiledName = filedNameMap.get(sortEntityName);//需要排序 获取排序字段
if (!StringUtils.isEmpty(sortFiledName)) {
jsonArray = JsonAndMapSortUtils.jsonArrayToSort(jsonArray, sortFiledName, true);
}
}
final JSONArray jsonArray1 = jsonArray;
Stream.iterate(0, integer -> integer + 1).limit(jsonArray1.size()).forEach(index -> {
Object value = jsonArray1.get(index);
String newRoot = "".equals(root) ? "[" + index + "]" : root + ".[" + index + "]";
fillInResultMap(value, newRoot, isNew);
});
}
}
/**
* 封装json转map后的数据
*
* @param value
* @param newRoot
* @param isNew 区别新旧json
*/
public void fillInResultMap(Object value, String newRoot, boolean isNew) {
if (value instanceof JSONObject || value instanceof JSONArray) {
convertJsonToMap(value, newRoot, isNew);
} else {
if (isNew) {
oldJsonToMap.put(newRoot, value);
} else {
newJsonToMap.put(newRoot, value);
}
}
}
/**
* 比较两个map,将不同的数据以map形式存储并返回
*
* @param oldJsonMap
* @param newJsonMap
* @return
*/
private Map<String, Object> compareTwoMaps(Map<String, Object> oldJsonMap, Map<String, Object> newJsonMap) {
//1.将newJsonMap的不同数据装进oldJsonMap,同时删除oldJsonMap中与newJsonMap相同的数据
newJsonMap.forEach((k, v) -> {
Map<String, Object> differenceMap = new HashMap<>();
String lastFieldKey = k.substring(k.lastIndexOf(".") + 1);
if (oldJsonMap.containsKey(k)) {
boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, lastFieldKey);
Object oldValue = oldJsonMap.get(k);
if (check || v.equals(oldValue)) {
oldJsonMap.remove(k);
} else {
differenceMap.put("oldValue", oldValue);
differenceMap.put("newValue", v);
oldJsonMap.put(k, differenceMap);
}
} else {
differenceMap.put("oldValue", "no exists " + k);
differenceMap.put("newValue", v);
oldJsonMap.put(k, differenceMap);
}
});
//2.统一oldJsonMap中newMap不存在的数据的数据结构,便于解析
oldJsonMap.forEach((k, v) -> {
String lastFieldKey = k.substring(k.lastIndexOf(".") + 1);
boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, lastFieldKey);
if (!check && !(v instanceof Map)) {
Map<String, Object> differenceMap = new HashMap<>();
differenceMap.put("oldValue", v);
differenceMap.put("newValue", "no exists " + k);
oldJsonMap.put(k, differenceMap);
}
});
return oldJsonMap;
}
/**
* 将已经找出不同数据的map根据key的层级结构封装成json返回
*
* @param map
* @return
*/
private String convertMapToJson(Map<String, Object> map) {
JSONObject resultJSONObject = new JSONObject();
for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> item = it.next();
String key = item.getKey();
Object value = item.getValue();
String[] paths = key.split("\\.");
int i = 0;
Object remarkObject = null;//用於深度標識對象
int indexAll = paths.length - 1;
while (i <= paths.length - 1) {
String path = paths[i];
if (i == 0) {
//初始化对象标识
if (resultJSONObject.containsKey(path)) {
remarkObject = resultJSONObject.get(path);
} else {
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
remarkObject = new JSONArray();
} else {
remarkObject = new JSONObject();
}
resultJSONObject.put(path, remarkObject);
} else {
resultJSONObject.put(path, value);
}
}
i++;
continue;
}
if (path.matches("\\[[0-9]+\\]")) {//匹配集合对象
int startIndex = path.lastIndexOf("[");
int endIndext = path.lastIndexOf("]");
int index = Integer.parseInt(path.substring(startIndex + 1, endIndext));
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
while (((JSONArray) remarkObject).size() <= index) {
if (((JSONArray) remarkObject).size() == index) {
((JSONArray) remarkObject).add(index, new JSONArray());
} else {
((JSONArray) remarkObject).add(null);
}
}
} else {
while (((JSONArray) remarkObject).size() <= index) {
if (((JSONArray) remarkObject).size() == index) {
((JSONArray) remarkObject).add(index, new JSONObject());
} else {
((JSONArray) remarkObject).add(null);
}
}
}
remarkObject = ((JSONArray) remarkObject).get(index);
} else {
while (((JSONArray) remarkObject).size() <= index) {
if (((JSONArray) remarkObject).size() == index) {
((JSONArray) remarkObject).add(index, value);
} else {
((JSONArray) remarkObject).add(null);
}
}
}
} else {
if (indexAll > i) {
if (paths[i + 1].matches("\\[[0-9]+\\]")) {
if (!((JSONObject) remarkObject).containsKey(path)) {
((JSONObject) remarkObject).put(path, new JSONArray());
}
} else {
if (!((JSONObject) remarkObject).containsKey(path)) {
((JSONObject) remarkObject).put(path, new JSONObject());
}
}
remarkObject = ((JSONObject) remarkObject).get(path);
} else {
((JSONObject) remarkObject).put(path, value);
}
}
i++;
}
}
return JSON.toJSONString(resultJSONObject);
}
public boolean isSort() {
return isSort;
}
public void setSort(boolean sort) {
isSort = sort;
}
public static void main(String[] args) {
String oldStr = "{key1:'aaa',key2:'bbb',key3:'cc'}";
String newStr = "{key1:'aaa',key2:'bb'}";
System.out.println(new JsonCompareUtils().compareTwoJson(oldStr, newStr));
System.out.println("\n========测试复杂json的比对============");
JsonCompareTest.compareTest();
}
/**
* 测试类
*/
static class JsonCompareTest {
public static void compareTest() {
String oldJson = MakeJsonCompareDatas.getJsonDataOldStr();
String newJson = MakeJsonCompareDatas.getJsonDataNewStr();
//对json报文中含有JsonArray类型的数据是否进行排序
JsonCompareUtils.isSort = true;
String compareResult = new JsonCompareUtils().compareTwoJson(oldJson, newJson);
System.out.println("oldJson==>" + oldJson);
System.out.println("newJson==>" + newJson);
System.out.println("\nisSort==" + isSort + "-->compareResult==>\n" + compareResult);
}
}
}
(5)依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
5. 关于C友提出的排序问题–修改后的代码如下
5.1 改动的地方
- 改check的位置(即:控制跳过不需对比字段的地方)
① 原先位置如下:在 方法compareTwoMaps
里
② 改后位置如下:
5.2 JsonCompareUtils 改后的代码
- 完整代码如下:
package com.liu.susu.json; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Stream; public class JsonCompareUtils { //标志位:对json报文中含有JsonArray类型的数据是否进行排序 private static boolean isSort; private Map<String, Object> oldJsonToMap = new LinkedHashMap<>(); private Map<String, Object> newJsonToMap = new LinkedHashMap<>(); //每一个实体里的排序字段 private static Map<String, String> filedNameMap = new HashMap<>(); static { filedNameMap.put("dogs", "dogNo"); filedNameMap.put("cats", "catNo"); } //可以跳过比对的字段 // private static String[] skipCompareFiledNameMap = {"dogAge", "catAge", "catName"}; private static String[] skipCompareFiledNameMap = {"key3"}; /** * 两json报文比对入口 * * @param oldJsonStr * @param newJsonStr * @return */ public String compareTwoJson(String oldJsonStr, String newJsonStr) { /** * 递归遍历json对象所有的key-value,以map形式的path:value进行存储 * 然后对两个map进行比较 */ convertJsonToMap(JSON.parseObject(oldJsonStr), "", false); convertJsonToMap(JSON.parseObject(newJsonStr), "", true); //获取比较结果 Map<String, Object> differenceMap = compareTwoMaps(oldJsonToMap, newJsonToMap); String diffJsonResult = convertMapToJson(differenceMap); return diffJsonResult; } /** * 将json数据转换为map存储--用于后续比较map * * @param json * @param root * @param isNew 区别新旧报文 */ private void convertJsonToMap(Object json, String root, boolean isNew) { if (json instanceof JSONObject) { JSONObject jsonObject = ((JSONObject) json); Iterator iterator = jsonObject.keySet().iterator(); while (iterator.hasNext()) { Object key = iterator.next(); Object value = jsonObject.get(key); String newRoot = "".equals(root) ? key + "" : root + "." + key; fillInResultMap(value, newRoot, isNew); } } else if (json instanceof JSONArray) { JSONArray jsonArray = (JSONArray) json; //将jsonArray进行排序 if (isSort) { //需要排序 String sortEntityName = root.substring(root.lastIndexOf(".") + 1); String sortFiledName = filedNameMap.get(sortEntityName);//需要排序 获取排序字段 if (!StringUtils.isEmpty(sortFiledName)) { jsonArray = JsonAndMapSortUtils.jsonArrayToSort(jsonArray, sortFiledName, true); } } final JSONArray jsonArray1 = jsonArray; Stream.iterate(0, integer -> integer + 1).limit(jsonArray1.size()).forEach(index -> { Object value = jsonArray1.get(index); String newRoot = "".equals(root) ? "[" + index + "]" : root + ".[" + index + "]"; fillInResultMap(value, newRoot, isNew); }); } } /** * 封装json转map后的数据 * * @param value * @param newRoot * @param isNew 区别新旧json */ public void fillInResultMap(Object value, String newRoot, boolean isNew) { if (value instanceof JSONObject || value instanceof JSONArray) { convertJsonToMap(value, newRoot, isNew); } else { //设置跳过比对的字段,直接不装入map boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, newRoot); if (!check){ if (isNew) { oldJsonToMap.put(newRoot, value); } else { newJsonToMap.put(newRoot, value); } } } } /** * 比较两个map,将不同的数据以map形式存储并返回 * * @param oldJsonMap * @param newJsonMap * @return */ private Map<String, Object> compareTwoMaps(Map<String, Object> oldJsonMap, Map<String, Object> newJsonMap) { //1.将newJsonMap的不同数据装进oldJsonMap,同时删除oldJsonMap中与newJsonMap相同的数据 newJsonMap.forEach((k, v) -> { Map<String, Object> differenceMap = new HashMap<>(); String lastFieldKey = k.substring(k.lastIndexOf(".") + 1); // boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, lastFieldKey); // if (!check){ if (oldJsonMap.containsKey(k)) { // boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, lastFieldKey); Object oldValue = oldJsonMap.get(k); if (v.equals(oldValue)) { oldJsonMap.remove(k); } else { differenceMap.put("oldValue", oldValue); differenceMap.put("newValue", v); oldJsonMap.put(k, differenceMap); } } else { differenceMap.put("oldValue", "no exists " + k); differenceMap.put("newValue", v); oldJsonMap.put(k, differenceMap); } // }else { // oldJsonMap.remove(k); // } }); //2.统一oldJsonMap中newMap不存在的数据的数据结构,便于解析 oldJsonMap.forEach((k, v) -> { String lastFieldKey = k.substring(k.lastIndexOf(".") + 1); // boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, lastFieldKey); // if (!check && !(v instanceof Map)) { if (!(v instanceof Map)) { Map<String, Object> differenceMap = new HashMap<>(); differenceMap.put("oldValue", v); differenceMap.put("newValue", "no exists " + k); oldJsonMap.put(k, differenceMap); } }); return oldJsonMap; } /** * 将已经找出不同数据的map根据key的层级结构封装成json返回 * * @param map * @return */ private String convertMapToJson(Map<String, Object> map) { JSONObject resultJSONObject = new JSONObject(); for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry<String, Object> item = it.next(); String key = item.getKey(); Object value = item.getValue(); String[] paths = key.split("\\."); int i = 0; Object remarkObject = null;//用於深度標識對象 int indexAll = paths.length - 1; while (i <= paths.length - 1) { String path = paths[i]; if (i == 0) { //初始化对象标识 if (resultJSONObject.containsKey(path)) { remarkObject = resultJSONObject.get(path); } else { if (indexAll > i) { if (paths[i + 1].matches("\\[[0-9]+\\]")) { remarkObject = new JSONArray(); } else { remarkObject = new JSONObject(); } resultJSONObject.put(path, remarkObject); } else { resultJSONObject.put(path, value); } } i++; continue; } if (path.matches("\\[[0-9]+\\]")) {//匹配集合对象 int startIndex = path.lastIndexOf("["); int endIndext = path.lastIndexOf("]"); int index = Integer.parseInt(path.substring(startIndex + 1, endIndext)); if (indexAll > i) { if (paths[i + 1].matches("\\[[0-9]+\\]")) { while (((JSONArray) remarkObject).size() <= index) { if (((JSONArray) remarkObject).size() == index) { ((JSONArray) remarkObject).add(index, new JSONArray()); } else { ((JSONArray) remarkObject).add(null); } } } else { while (((JSONArray) remarkObject).size() <= index) { if (((JSONArray) remarkObject).size() == index) { ((JSONArray) remarkObject).add(index, new JSONObject()); } else { ((JSONArray) remarkObject).add(null); } } } remarkObject = ((JSONArray) remarkObject).get(index); } else { while (((JSONArray) remarkObject).size() <= index) { if (((JSONArray) remarkObject).size() == index) { ((JSONArray) remarkObject).add(index, value); } else { ((JSONArray) remarkObject).add(null); } } } } else { if (indexAll > i) { if (paths[i + 1].matches("\\[[0-9]+\\]")) { if (!((JSONObject) remarkObject).containsKey(path)) { ((JSONObject) remarkObject).put(path, new JSONArray()); } } else { if (!((JSONObject) remarkObject).containsKey(path)) { ((JSONObject) remarkObject).put(path, new JSONObject()); } } remarkObject = ((JSONObject) remarkObject).get(path); } else { ((JSONObject) remarkObject).put(path, value); } } i++; } } return JSON.toJSONString(resultJSONObject); } public boolean isSort() { return isSort; } public void setSort(boolean sort) { isSort = sort; } public static void main(String[] args) { String oldStr = "{key1:'aaa',key2:'bbb'}"; String newStr = "{key1:'aaa',key2:'bbb',key3:'c'}"; // String newStr = "{key1:'aaa',key2:'bbb'}"; System.out.println(new JsonCompareUtils().compareTwoJson(oldStr, newStr)); // System.out.println("==========================="); // String json1 = "{\"key1\":\"a\",\"key2\":\"b\",\"key3\":\"c\"}"; // String json2 = "{\"key1\":\"a\",\"key2\":\"b\"}"; // System.out.println(new JsonCompareUtils().compareTwoJson(json1, json2)); System.out.println("\n========测试复杂json的比对============"); JsonCompareTest.compareTest(); } /** * 测试类 */ static class JsonCompareTest { public static void compareTest() { String oldJson = MakeJsonCompareDatas.getJsonDataOldStr(); String newJson = MakeJsonCompareDatas.getJsonDataNewStr(); //对json报文中含有JsonArray类型的数据是否进行排序 JsonCompareUtils.isSort = true; String compareResult = new JsonCompareUtils().compareTwoJson(oldJson, newJson); System.out.println("oldJson==>" + oldJson); System.out.println("newJson==>" + newJson); System.out.println("\nisSort==" + isSort + "-->compareResult==>\n" + compareResult); } } }