• Json字符串内容比较-超实用版


    背景

    之前有类似接口diff对比,数据对比的测试需求,涉及到json格式的数据对比,调研了几个大神们分享的代码,选了一个最符合自己需求的研究了下。

    说明

    这个对比方法,支持JsonObject和JsonArray类型的数据对比,支持:

    • 深度的对比:list变化(个数、内容)、层级结构变化

    • 字段的对比:新增、修改、删除数据可察觉,能找到对应的旧数据

    • 支持特定字段忽略对比

    输出的对比结果格式为:

    源码分为JsonCompareUtils, JsonAndMapSortUtils两个类,对比入口是compareTwoJson方法

    核心逻辑在JsonCompareUtils类中,JsonAndMapSortUtils主要做过程中的数据排序功能,相对独立。

     上源码:

    1. import com.alibaba.fastjson.JSON;
    2. import com.alibaba.fastjson.JSONArray;
    3. import com.alibaba.fastjson.JSONObject;
    4. import org.apache.commons.lang.ArrayUtils;
    5. import org.apache.commons.lang.StringUtils;
    6. import java.util.HashMap;
    7. import java.util.Iterator;
    8. import java.util.LinkedHashMap;
    9. import java.util.Map;
    10. import java.util.stream.Stream;
    11. public class JsonCompareUtils {
    12. //标志位:对json报文中含有JsonArray类型的数据是否进行排序
    13. private static boolean isSort;
    14. private Map oldJsonToMap = new LinkedHashMap<>();
    15. private Map newJsonToMap = new LinkedHashMap<>();
    16. //每一个实体里的排序字段
    17. private static Map filedNameMap = new HashMap<>();
    18. static {
    19. filedNameMap.put("ojb1", "id");
    20. filedNameMap.put("ojb2", "id");
    21. }
    22. //可以跳过比对的字段
    23. private static String[] skipCompareFiledNameMap = {"createTime"};
    24. /**
    25. * 两json报文比对入口
    26. *
    27. * @param oldJsonStr
    28. * @param newJsonStr
    29. * @return
    30. */
    31. public String compare2Json(String oldJsonStr, String newJsonStr) {
    32. /**
    33. * 递归遍历json对象所有的key-value,以map形式的path:value进行存储
    34. * 然后对两个map进行比较
    35. */
    36. convertJsonToMap(JSON.parseObject(oldJsonStr), "", false);
    37. convertJsonToMap(JSON.parseObject(newJsonStr), "", true);
    38. //获取比较结果
    39. Map differenceMap = compareTwoMaps(oldJsonToMap, newJsonToMap);
    40. String diffJsonResult = convertMapToJson(differenceMap);
    41. return diffJsonResult;
    42. }
    43. /**
    44. * 将json数据转换为map存储--用于后续比较map
    45. *
    46. * @param json
    47. * @param root
    48. * @param isNew 区别新旧报文
    49. */
    50. private void convertJsonToMap(Object json, String root, boolean isNew) {
    51. if (json instanceof JSONObject) {
    52. JSONObject jsonObject = ((JSONObject) json);
    53. Iterator iterator = jsonObject.keySet().iterator();
    54. while (iterator.hasNext()) {
    55. Object key = iterator.next();
    56. Object value = jsonObject.get(key);
    57. String newRoot = "".equals(root) ? key + "" : root + "." + key;
    58. fillInResultMap(value, newRoot, isNew);
    59. }
    60. } else if (json instanceof JSONArray) {
    61. JSONArray jsonArray = (JSONArray) json;
    62. //将jsonArray进行排序
    63. if (isSort) {
    64. //需要排序
    65. String sortEntityName = root.substring(root.lastIndexOf(".") + 1);
    66. //需要排序 获取排序字段
    67. String sortFiledName = filedNameMap.get(sortEntityName);
    68. if (!StringUtils.isEmpty(sortFiledName)) {
    69. jsonArray = JsonAndMapSortUtils.jsonArrayToSort(jsonArray, sortFiledName, true);
    70. }
    71. }
    72. final JSONArray jsonArray1 = jsonArray;
    73. Stream.iterate(0, integer -> integer + 1).limit(jsonArray1.size()).forEach(index -> {
    74. Object value = jsonArray1.get(index);
    75. String newRoot = "".equals(root) ? "[" + index + "]" : root + ".[" + index + "]";
    76. fillInResultMap(value, newRoot, isNew);
    77. });
    78. }
    79. }
    80. /**
    81. * 封装json转map后的数据
    82. *
    83. * @param value
    84. * @param newRoot
    85. * @param isNew 区别新旧json
    86. */
    87. public void fillInResultMap(Object value, String newRoot, boolean isNew) {
    88. if (value instanceof JSONObject || value instanceof JSONArray) {
    89. convertJsonToMap(value, newRoot, isNew);
    90. } else {
    91. //设置跳过比对的字段,直接不装入map
    92. boolean check = ArrayUtils.contains(JsonCompareUtils.skipCompareFiledNameMap, newRoot);
    93. if (!check){
    94. if (!isNew) {
    95. oldJsonToMap.put(newRoot, value);
    96. } else {
    97. newJsonToMap.put(newRoot, value);
    98. }
    99. }
    100. }
    101. }
    102. /**
    103. * 比较两个map,将不同的数据以map形式存储并返回
    104. *
    105. * @param oldJsonMap
    106. * @param newJsonMap
    107. * @return
    108. */
    109. private Map compareTwoMaps(Map oldJsonMap, Map newJsonMap) {
    110. //1.将newJsonMap的不同数据装进oldJsonMap,同时删除oldJsonMap中与newJsonMap相同的数据
    111. newJsonMap.forEach((k, v) -> {
    112. Map differenceMap = new HashMap<>();
    113. if (oldJsonMap.containsKey(k)) {
    114. Object oldValue = oldJsonMap.get(k);
    115. if (v.equals(oldValue)) {
    116. oldJsonMap.remove(k);
    117. } else {
    118. differenceMap.put("oldValue", oldValue);
    119. differenceMap.put("newValue", v);
    120. oldJsonMap.put(k, differenceMap);
    121. }
    122. } else {
    123. differenceMap.put("oldValue", "no exists " + k);
    124. differenceMap.put("newValue", v);
    125. oldJsonMap.put(k, differenceMap);
    126. }
    127. });
    128. //2.统一oldJsonMap中newMap不存在的数据的数据结构,便于解析
    129. oldJsonMap.forEach((k, v) -> {
    130. if (!(v instanceof Map)) {
    131. Map differenceMap = new HashMap<>();
    132. differenceMap.put("oldValue", v);
    133. differenceMap.put("newValue", "no exists " + k);
    134. oldJsonMap.put(k, differenceMap);
    135. }
    136. });
    137. return oldJsonMap;
    138. }
    139. /**
    140. * 将已经找出不同数据的map根据key的层级结构封装成json返回
    141. *
    142. * @param map
    143. * @return
    144. */
    145. private String convertMapToJson(Map map) {
    146. JSONObject resultJSONObject = new JSONObject();
    147. for (Iterator> it = map.entrySet().iterator(); it.hasNext(); ) {
    148. Map.Entry item = it.next();
    149. String key = item.getKey();
    150. Object value = item.getValue();
    151. String[] paths = key.split("\\.");
    152. int i = 0;
    153. //用於深度標識對象
    154. Object remarkObject = null;
    155. int indexAll = paths.length - 1;
    156. while (i <= paths.length - 1) {
    157. String path = paths[i];
    158. if (i == 0) {
    159. //初始化对象标识
    160. if (resultJSONObject.containsKey(path)) {
    161. remarkObject = resultJSONObject.get(path);
    162. } else {
    163. if (indexAll > i) {
    164. if (paths[i + 1].matches("\\[[0-9]+\\]")) {
    165. remarkObject = new JSONArray();
    166. } else {
    167. remarkObject = new JSONObject();
    168. }
    169. resultJSONObject.put(path, remarkObject);
    170. } else {
    171. resultJSONObject.put(path, value);
    172. }
    173. }
    174. i++;
    175. continue;
    176. }
    177. //匹配集合对象
    178. if (path.matches("\\[[0-9]+\\]")) {
    179. int startIndex = path.lastIndexOf("[");
    180. int endIndext = path.lastIndexOf("]");
    181. int index = Integer.parseInt(path.substring(startIndex + 1, endIndext));
    182. if (indexAll > i) {
    183. if (paths[i + 1].matches("\\[[0-9]+\\]")) {
    184. while (((JSONArray) remarkObject).size() <= index) {
    185. if (((JSONArray) remarkObject).size() == index) {
    186. ((JSONArray) remarkObject).add(index, new JSONArray());
    187. } else {
    188. ((JSONArray) remarkObject).add(null);
    189. }
    190. }
    191. } else {
    192. while (((JSONArray) remarkObject).size() <= index) {
    193. if (((JSONArray) remarkObject).size() == index) {
    194. ((JSONArray) remarkObject).add(index, new JSONObject());
    195. } else {
    196. ((JSONArray) remarkObject).add(null);
    197. }
    198. }
    199. }
    200. remarkObject = ((JSONArray) remarkObject).get(index);
    201. } else {
    202. while (((JSONArray) remarkObject).size() <= index) {
    203. if (((JSONArray) remarkObject).size() == index) {
    204. ((JSONArray) remarkObject).add(index, value);
    205. } else {
    206. ((JSONArray) remarkObject).add(null);
    207. }
    208. }
    209. }
    210. } else {
    211. if (indexAll > i) {
    212. if (paths[i + 1].matches("\\[[0-9]+\\]")) {
    213. if (!((JSONObject) remarkObject).containsKey(path)) {
    214. ((JSONObject) remarkObject).put(path, new JSONArray());
    215. }
    216. } else {
    217. if (!((JSONObject) remarkObject).containsKey(path)) {
    218. ((JSONObject) remarkObject).put(path, new JSONObject());
    219. }
    220. }
    221. remarkObject = ((JSONObject) remarkObject).get(path);
    222. } else {
    223. ((JSONObject) remarkObject).put(path, value);
    224. }
    225. }
    226. i++;
    227. }
    228. }
    229. return JSON.toJSONString(resultJSONObject);
    230. }
    231. public boolean isSort() {
    232. return isSort;
    233. }
    234. public void setSort(boolean sort) {
    235. isSort = sort;
    236. }
    237. public static void main(String[] args) {
    238. String oldStr = "{\"abilityLevel\":\"2020101240000000000002\",\"activityId\":\"202208301413310000412\",\"addLibTime\":1662083360000,\"ansKnowModelIds\":\"1#201812051814150000475\",\"answer\":\"AB\",\"ascriptionItemLib\":\"0\",\"attributeList\":[{\"content\":\"

      A

      \",\"id\":\"202209020949170005783\",\"itemId\":\"202209020949150001521\",\"knowModelRelId\":\"201812051814150000475\",\"name\":\"A\",\"sort\":1,\"type\":\"option\"},{\"content\":\"

      B

      \",\"id\":\"202209020949170005784\",\"itemId\":\"202209020949150001521\",\"name\":\"B\",\"sort\":2,\"type\":\"option\"},{\"content\":\"

      C

      \",\"id\":\"202209020949170005785\",\"itemId\":\"202209020949150001521\",\"name\":\"C\",\"sort\":3,\"type\":\"option\"},{\"content\":\"

      D

      \",\"id\":\"202209020949170005786\",\"itemId\":\"202209020949150001521\",\"name\":\"D\",\"sort\":4,\"type\":\"option\"},{\"content\":\"11\",\"createTime\":1662083368000,\"createTimeString\":\"2022-09-02 09:49:28\",\"creater\":\"10001\",\"id\":\"202209020949170005787\",\"itemId\":\"202209020949150001521\",\"name\":\"大纲依据\",\"searchFlag\":\"1\",\"sort\":1,\"subItemTypeAttrId\":\"100041\",\"type\":\"expandAttr\"},{\"content\":\"11\",\"createTime\":1662083370000,\"createTimeString\":\"2022-09-02 09:49:30\",\"creater\":\"10001\",\"id\":\"202209020949280005788\",\"itemId\":\"202209020949150001521\",\"name\":\"教材依据\",\"searchFlag\":\"1\",\"sort\":2,\"subItemTypeAttrId\":\"100042\",\"type\":\"expandAttr\"},{\"content\":\"11\",\"createTime\":1662083371000,\"createTimeString\":\"2022-09-02 09:49:31\",\"creater\":\"10001\",\"id\":\"202209020949290005789\",\"itemId\":\"202209020949150001521\",\"name\":\"关键字\",\"searchFlag\":\"1\",\"sort\":3,\"subItemTypeAttrId\":\"100043\",\"type\":\"expandAttr\"}],\"auditCount\":0,\"auditStatus\":0,\"childItemList\":[],\"createTime\":1662083350000,\"createTimeString\":\"2022-09-02 09:49:10\",\"creater\":\"10001\",\"delFlag\":\"0\",\"difficult\":\"3\",\"id\":\"202209020949150001521\",\"isComposite\":0,\"isTopVersion\":0,\"itemCode\":\"KJCJ202209020949140001501\",\"itemContent\":\"

      2.按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)

      \",\"itemKnowledgeList\":[{\"id\":\"202209020949300001537\",\"itemId\":\"202209020949150001521\",\"knowledgeId\":\"1240004\",\"knowledgeStr\":\"2020年经济法基础/第一章总论/第一节法律基础/一、法和法律/(一)法和法律的概念\",\"level\":\"1\",\"pid\":\"202209020946580001519\"}],\"itemLevel\":1,\"itemSource\":\"202208301413310000412\",\"itemTypeId\":\"4\",\"itemVersion\":\"1\",\"keyWord\":\"\",\"knowledgeId\":\"1240004\",\"knowledgeStr\":\"2020年经济法基础/第一章总论/第一节法律基础/一、法和法律/(一)法和法律的概念\",\"knowledgeSyllabusName\":\"2020年经济法基础\",\"lockStatus\":1,\"modifyChlidItems\":[],\"paperCount\":0,\"paperItemRelation\":{},\"paperStructure\":{},\"parentId\":\"202209020946580001519\",\"processedItemContent\":\"

      2.按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)

      \",\"score\":2,\"sort\":2,\"sourceType\":\"2\",\"structId\":\"\",\"subItemTypeId\":\"20180228155501000004\",\"subjectId\":\"4\",\"updateTime\":1662083350000,\"updateTimeString\":\"2022-09-02 09:49:10\",\"updater\":\"10001\"}"
      ;
    239. String newStr = "{\"abilityLevel\":\"2020101240000000000002\",\"activityId\":\"202208301413310000412\",\"addLibTime\":1662083360000,\"analysis\":\"\",\"ansKnowModelIds\":\"1#201812051814150000475\",\"answer\":\"AB\",\"ascriptionItemLib\":\"0\",\"attributeList\":[{\"content\":\"

      A

      \",\"id\":\"202209021135210005855\",\"itemId\":\"202209020949150001521\",\"knowModelRelId\":\"201812051814150000475\",\"name\":\"A\",\"sort\":1,\"type\":\"option\"},{\"content\":\"

      B

      \",\"id\":\"202209021135210005856\",\"itemId\":\"202209020949150001521\",\"name\":\"B\",\"sort\":2,\"type\":\"option\"},{\"content\":\"

      C

      \",\"id\":\"202209021135210005857\",\"itemId\":\"202209020949150001521\",\"name\":\"C\",\"sort\":3,\"type\":\"option\"},{\"content\":\"

      D

      \",\"id\":\"202209021135210005858\",\"itemId\":\"202209020949150001521\",\"name\":\"D\",\"sort\":4,\"type\":\"option\"},{\"content\":\"11\",\"createTime\":1662089721000,\"createTimeString\":\"2022-09-02 11:35:21\",\"creater\":\"10001\",\"id\":\"202209021135210005859\",\"itemId\":\"202209020949150001521\",\"name\":\"大纲依据\",\"searchFlag\":\"1\",\"sort\":1,\"subItemTypeAttrId\":\"100041\",\"type\":\"expandAttr\"},{\"content\":\"11\",\"createTime\":1662089721000,\"createTimeString\":\"2022-09-02 11:35:21\",\"creater\":\"10001\",\"id\":\"202209021135210005860\",\"itemId\":\"202209020949150001521\",\"name\":\"教材依据\",\"searchFlag\":\"1\",\"sort\":2,\"subItemTypeAttrId\":\"100042\",\"type\":\"expandAttr\"},{\"content\":\"11\",\"createTime\":1662089722000,\"createTimeString\":\"2022-09-02 11:35:22\",\"creater\":\"10001\",\"id\":\"202209021135210005861\",\"itemId\":\"202209020949150001521\",\"name\":\"关键字\",\"searchFlag\":\"1\",\"sort\":3,\"subItemTypeAttrId\":\"100043\",\"type\":\"expandAttr\"}],\"auditCount\":0,\"auditStatus\":0,\"childItemList\":[],\"createTime\":1662083350000,\"createTimeString\":\"2022-09-02 09:49:10\",\"creater\":\"10001\",\"delFlag\":\"0\",\"difficult\":\"5\",\"id\":\"202209020949150001521\",\"isComposite\":0,\"isTopVersion\":0,\"itemCode\":\"KJCJ202209020949140001501\",\"itemContent\":\"

      2.按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)

      \",\"itemKnowledgeList\":[{\"id\":\"202209021135230001557\",\"itemId\":\"202209020949150001521\",\"knowledgeId\":\"1240004\",\"knowledgeStr\":\"2020年经济法基础/第一章总论/第一节法律基础/一、法和法律/(一)法和法律的概念\",\"level\":\"1\",\"pid\":\"202209020946580001519\"}],\"itemLevel\":1,\"itemSource\":\"202208301413310000412\",\"itemTypeId\":\"4\",\"itemVersion\":\"2\",\"keyWord\":\"\",\"knowledgeId\":\"1240004\",\"knowledgeStr\":\"2020年经济法基础/第一章总论/第一节法律基础/一、法和法律/(一)法和法律的概念\",\"knowledgeSyllabusName\":\"2020年经济法基础\",\"lockStatus\":1,\"modifyChlidItems\":[],\"paperCount\":0,\"paperItemRelation\":{},\"paperStructure\":{},\"parentId\":\"202209020946580001519\",\"processedItemContent\":\"

      2.按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)

      \",\"score\":2,\"sort\":2,\"sourceType\":\"2\",\"structId\":\"\",\"subItemTypeId\":\"20180228155501000004\",\"subjectId\":\"4\",\"updateTime\":1662089720000,\"updateTimeString\":\"2022-09-02 11:35:20\",\"updater\":\"10001\"}"
      ;
    240. System.out.println(new JsonCompareUtils().compare2Json(oldStr, newStr));
    241. System.out.println("\n========测试复杂json的比对============");
    242. }
    243. }

    JsonAndMapSortUtils 用于jsonArray排序
    
    1. import com.alibaba.fastjson.JSONArray;
    2. import com.alibaba.fastjson.JSONObject;
    3. import sun.misc.ASCIICaseInsensitiveComparator;
    4. import java.util.*;
    5. public class JsonAndMapSortUtils {
    6. /**
    7. * map排序
    8. * @param map
    9. * @param keySort
    10. * @param
    11. * @param
    12. * @return
    13. */
    14. public static List mapByKeyToSort(Map map , final Comparator keySort){
    15. List> entryList = new ArrayList<>(map.entrySet());
    16. Collections.sort(entryList, (o1, o2) -> keySort.compare(o1.getKey(),o2.getKey()));
    17. System.out.println("排序=====");
    18. entryList.forEach(m->{
    19. System.out.println(m.getKey()+"===>"+m.getValue());
    20. });
    21. return entryList;
    22. }
    23. /**
    24. * JSONArray排序
    25. * @param jsonArray
    26. * @param fildName
    27. * @param isAsc
    28. * @return
    29. */
    30. public static JSONArray jsonArrayToSort(JSONArray jsonArray,final String fildName,final boolean isAsc){
    31. JSONArray afterSortJsonArray = new JSONArray();
    32. List objectList = new ArrayList<>();
    33. jsonArray.forEach(obj ->{
    34. objectList.add((JSONObject)obj);
    35. });
    36. Collections.sort(objectList, (o1, o2) -> {
    37. String fildValueA = o1.getString(fildName);
    38. String fildValueB = o2.getString(fildName);
    39. if (isAsc){
    40. return fildValueA.compareTo(fildValueB);
    41. }
    42. return fildValueB.compareTo(fildValueA);
    43. });
    44. objectList.forEach(obj->{
    45. afterSortJsonArray.add(obj);
    46. });
    47. return afterSortJsonArray;
    48. }
    49. /**
    50. *准备map测试数据
    51. */
    52. public static Map getMapData(){
    53. LinkedHashMap map = new LinkedHashMap<>();
    54. map.put("key1","测试1");
    55. map.put("key3","测试3");
    56. map.put("key5","测试5");
    57. map.put("key2","测试2");
    58. map.put("key4","测试4");
    59. return map;
    60. }
    61. /**
    62. *准备json测试数据
    63. */
    64. public static JSONArray getJsonArrayData(){
    65. JSONArray jsonArray = new JSONArray();
    66. JSONObject jsonObject1 = new JSONObject();
    67. jsonObject1.put("userId","1001");
    68. jsonObject1.put("name","测试1");
    69. jsonArray.add(jsonObject1);
    70. JSONObject jsonObject3 = new JSONObject();
    71. jsonObject3.put("userId","1003");
    72. jsonObject3.put("name","测试3");
    73. jsonArray.add(jsonObject3);
    74. JSONObject jsonObject2 = new JSONObject();
    75. jsonObject2.put("userId","1002");
    76. jsonObject2.put("name","测试2");
    77. jsonArray.add(jsonObject2);
    78. return jsonArray;
    79. }
    80. public static void main(String[] args) {
    81. Map map = JsonAndMapSortUtils.getMapData();
    82. JSONArray jsonArray = JsonAndMapSortUtils.getJsonArrayData();
    83. List afterSortMap = JsonAndMapSortUtils.mapByKeyToSort(map,new ASCIICaseInsensitiveComparator());
    84. JSONArray afterSortJsonArray_isAsc = JsonAndMapSortUtils.jsonArrayToSort(jsonArray,"userId",true);
    85. JSONArray afterSortJsonArray_noAsc = JsonAndMapSortUtils.jsonArrayToSort(jsonArray,"userId",false);
    86. System.out.println("map排序前:"+map);
    87. System.out.println("map排序后:"+afterSortMap+"\n");
    88. System.out.println("JsonArray排序前:"+jsonArray);
    89. System.out.println("JsonArray排序后==》升序:"+afterSortJsonArray_isAsc);
    90. System.out.println("JsonArray排序后==》降序:"+afterSortJsonArray_noAsc);
    91. }
    92. }


    源码走读
    整个源码调用链路如下图,简单来说过程就是:object拆分解析-新旧数据逐个对比-结果信息组装三个步骤

  • 相关阅读:
    QT之QML开发 行列布局,流布局,网格布局
    第12章 - 执行引擎
    vulnhub靶机DC5
    C++ vector 功能强大的数组
    【问题记录】解决Git上传文件到GitHub时收到 “GH001: Large files detected” 错误信息!
    Could not resolve dependencies for project
    C++模拟OpenGL库——图片处理及纹理系统(三):图片缩放操作:简单插值&二次线性插值
    《深入浅出.NET框架设计与实现》笔记6.2——ASP.NET Core应用程序多种运行模式之二——IIS 服务承载
    SQL Server教程 - SQL Server 安全管理(SECURITY)
    SQL interview Questions
  • 原文地址:https://blog.csdn.net/hnhygkx/article/details/132707556