Json字符串内容比较超实用教程
背景
之前有类似接口diff对比,数据对比的测试需求,涉及到json格式的数据对比,调研了几个大神们分享的代码,选了一个最符合自己需求的研究了下。
说明
这个对比方法,支持JsonObject和JsonArray类型的数据对比,支持:
- 深度的对比:list变化(个数、内容)、层级结构变化
- 字段的对比:新增、修改、删除数据可察觉,能找到对应的旧数据
- 支持特定字段忽略对比
输出的对比结果格式为:
源码分为JsonCompareUtils, JsonAndMapSortUtils两个类,对比入口是compareTwoJson方法
核心逻辑在JsonCompareUtils类中,JsonAndMapSortUtils主要做过程中的数据排序功能,相对独立。
上源码:
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("ojb1", "id"); filedNameMap.put("ojb2", "id"); } //可以跳过比对的字段 private static String[] skipCompareFiledNameMap = {"createTime"}; /** * 两json报文比对入口 * * @param oldJsonStr * @param newJsonStr * @return */ public String compare2Json(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<>(); if (oldJsonMap.containsKey(k)) { 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); } }); //2.统一oldJsonMap中newMap不存在的数据的数据结构,便于解析 oldJsonMap.forEach((k, v) -> { 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 = "{\"abilityLevel\":\"2020101240000000000002\",\"activityId\":\"202208301413310000412\",\"addLibTime\":1662083360000,\"ansKnowModelIds\":\"1#201812051814150000475\",\"answer\":\"AB\",\"ascriptionItemLib\":\"0\",\"attributeList\":[{\"content\":\"<p>A</p>\",\"id\":\"202209020949170005783\",\"itemId\":\"202209020949150001521\",\"knowModelRelId\":\"201812051814150000475\",\"name\":\"A\",\"sort\":1,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">B</p>\",\"id\":\"202209020949170005784\",\"itemId\":\"202209020949150001521\",\"name\":\"B\",\"sort\":2,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">C</p>\",\"id\":\"202209020949170005785\",\"itemId\":\"202209020949150001521\",\"name\":\"C\",\"sort\":3,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">D</p>\",\"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\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">2.<span style=\\\"color: #333333; font-family: Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 14px; text-align: justify; background-color: #FFFFFF;\\\">按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)</span></p>\",\"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\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">2.<span style=\\\"color: #333333; font-family: Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 14px; text-align: justify; background-color: #FFFFFF;\\\">按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)</span></p>\",\"score\":2,\"sort\":2,\"sourceType\":\"2\",\"structId\":\"\",\"subItemTypeId\":\"20180228155501000004\",\"subjectId\":\"4\",\"updateTime\":1662083350000,\"updateTimeString\":\"2022-09-02 09:49:10\",\"updater\":\"10001\"}"; String newStr = "{\"abilityLevel\":\"2020101240000000000002\",\"activityId\":\"202208301413310000412\",\"addLibTime\":1662083360000,\"analysis\":\"\",\"ansKnowModelIds\":\"1#201812051814150000475\",\"answer\":\"AB\",\"ascriptionItemLib\":\"0\",\"attributeList\":[{\"content\":\"<p>A</p>\",\"id\":\"202209021135210005855\",\"itemId\":\"202209020949150001521\",\"knowModelRelId\":\"201812051814150000475\",\"name\":\"A\",\"sort\":1,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">B</p>\",\"id\":\"202209021135210005856\",\"itemId\":\"202209020949150001521\",\"name\":\"B\",\"sort\":2,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">C</p>\",\"id\":\"202209021135210005857\",\"itemId\":\"202209020949150001521\",\"name\":\"C\",\"sort\":3,\"type\":\"option\"},{\"content\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">D</p>\",\"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\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">2.<span style=\\\"color: #333333; font-family: Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 14px; text-align: justify; background-color: #FFFFFF;\\\">按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)</span></p>\",\"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\":\"<p style=\\\"font-family:'Times New Roman','宋体';font-size:12pt;padding:0;margin:0;\\\">2.<span style=\\\"color: #333333; font-family: Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 14px; text-align: justify; background-color: #FFFFFF;\\\">按照并购双方行业相关性划分,判断并购类型(横向并购or混合并购)</span></p>\",\"score\":2,\"sort\":2,\"sourceType\":\"2\",\"structId\":\"\",\"subItemTypeId\":\"20180228155501000004\",\"subjectId\":\"4\",\"updateTime\":1662089720000,\"updateTimeString\":\"2022-09-02 11:35:20\",\"updater\":\"10001\"}"; System.out.println(new JsonCompareUtils().compare2Json(oldStr, newStr)); System.out.println("\n========测试复杂json的比对============"); } }
JsonAndMapSortUtils 用于jsonArray排序
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import sun.misc.ASCIICaseInsensitiveComparator; import java.util.*; public class JsonAndMapSortUtils { /** * map排序 * @param map * @param keySort * @param <k> * @param <v> * @return */ public static <k,v> List mapByKeyToSort(Map<k,v> map , final Comparator keySort){ List<Map.Entry<k,v>> entryList = new ArrayList<>(map.entrySet()); Collections.sort(entryList, (o1, o2) -> keySort.compare(o1.getKey(),o2.getKey())); System.out.println("排序====="); entryList.forEach(m->{ System.out.println(m.getKey()+"===>"+m.getValue()); }); return entryList; } /** * JSONArray排序 * @param jsonArray * @param fildName * @param isAsc * @return */ public static JSONArray jsonArrayToSort(JSONArray jsonArray,final String fildName,final boolean isAsc){ JSONArray afterSortJsonArray = new JSONArray(); List<JSONObject> objectList = new ArrayList<>(); jsonArray.forEach(obj ->{ objectList.add((JSONObject)obj); }); Collections.sort(objectList, (o1, o2) -> { String fildValueA = o1.getString(fildName); String fildValueB = o2.getString(fildName); if (isAsc){ return fildValueA.compareTo(fildValueB); } return fildValueB.compareTo(fildValueA); }); objectList.forEach(obj->{ afterSortJsonArray.add(obj); }); return afterSortJsonArray; } /** *准备map测试数据 */ public static Map<String,String> getMapData(){ LinkedHashMap<String,String> map = new LinkedHashMap<>(); map.put("key1","测试1"); map.put("key3","测试3"); map.put("key5","测试5"); map.put("key2","测试2"); map.put("key4","测试4"); return map; } /** *准备json测试数据 */ public static JSONArray getJsonArrayData(){ JSONArray jsonArray = new JSONArray(); JSONObject jsonObject1 = new JSONObject(); jsonObject1.put("userId","1001"); jsonObject1.put("name","测试1"); jsonArray.add(jsonObject1); JSONObject jsonObject3 = new JSONObject(); jsonObject3.put("userId","1003"); jsonObject3.put("name","测试3"); jsonArray.add(jsonObject3); JSONObject jsonObject2 = new JSONObject(); jsonObject2.put("userId","1002"); jsonObject2.put("name","测试2"); jsonArray.add(jsonObject2); return jsonArray; } public static void main(String[] args) { Map<String,String> map = JsonAndMapSortUtils.getMapData(); JSONArray jsonArray = JsonAndMapSortUtils.getJsonArrayData(); List afterSortMap = JsonAndMapSortUtils.mapByKeyToSort(map,new ASCIICaseInsensitiveComparator()); JSONArray afterSortJsonArray_isAsc = JsonAndMapSortUtils.jsonArrayToSort(jsonArray,"userId",true); JSONArray afterSortJsonArray_noAsc = JsonAndMapSortUtils.jsonArrayToSort(jsonArray,"userId",false); System.out.println("map排序前:"+map); System.out.println("map排序后:"+afterSortMap+"\n"); System.out.println("JsonArray排序前:"+jsonArray); System.out.println("JsonArray排序后==》升序:"+afterSortJsonArray_isAsc); System.out.println("JsonArray排序后==》降序:"+afterSortJsonArray_noAsc); } }
源码走读
整个源码调用链路如下图,简单来说过程就是:object拆分解析-新旧数据逐个对比-结果信息组装三个步骤
到此这篇关于Json字符串内容比较-超实用版的文章就介绍到这了,更多相关Json字符串内容比较内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot中使用websocket出现404的解决方法
在Springboot中使用websocket时,本地开发环境可以正常运行,但部署到服务器环境出现404问题,所以本文小编讲给大家详细介绍一下SpringBoot中使用websocket出现404的解决方法,需要的朋友可以参考下2023-09-09SpringBoot整合Hashids实现数据ID加密隐藏的全过程
这篇文章主要为大家详细介绍了SpringBoot整合Hashids实现数据ID加密隐藏的全过程,文中的示例代码讲解详细,对大家的学习或工作有一定的帮助,感兴趣的小伙伴可以跟随小编一起学习一下2024-01-01Win10 Java jdk14.0.2安装及环境变量配置详细教程
这篇文章主要介绍了Win10 Java jdk14.0.2安装及环境变量配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-08-08Java spring注解@PostConstruct实战案例讲解
我们在Spring项目中经常会遇到@PostConstruct注解,可能有的伙伴对这个注解很陌生,下面这篇文章主要给大家介绍了关于Java spring注解@PostConstruct实战案例讲解的相关资料,需要的朋友可以参考下2023-12-12
最新评论