JSON数据的差异对比工具(Java版)

这篇具有很好参考价值的文章主要介绍了JSON数据的差异对比工具(Java版)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

背景

说明

源码

源码走读

其他


背景

        之前有类似接口diff对比,数据对比的测试需求,涉及到json格式的数据对比,调研了几个大神们分享的代码,选了一个最符合自己需求的研究了下。(可惜原文链接找不到了,原始作者看到了可以私信我下)

说明

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

  • 深度的对比:list变化(个数、内容)、层级结构变化
  • 字段的对比:新增、修改、删除数据可察觉,能找到对应的旧数据
  • 支持特定字段忽略对比

输出的对比结果格式为:

json对比工具,java,json,开发语言,测试工具

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

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

源码

package com.xhzyqa.transcodetest.utils;


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'}";
        System.out.println(new JsonCompareUtils().compareTwoJson(oldStr, newStr));
        System.out.println("\n========测试复杂json的比对============");
    }

    /**
     * 测试类

    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);



        }
    }
     */
}


package com.xhzyqa.transcodetest.utils;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import sun.misc.ASCIICaseInsensitiveComparator;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

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.Entry<k, v>>(map.entrySet());
        Collections.sort(entryList, new Comparator<Map.Entry<k, v>>() {
            public int compare(Map.Entry<k, v> o1, Map.Entry<k, v> o2) {
                return keySort.compare(o1.getKey(),o2.getKey());
            }
        });
        //return (Map<k,v>)entryList.stream().collect(Collectors.toMap(Map.Entry<k,v>::getKey, Function.identity(), (key1, key2) -> key2));
        Map<String,String> afterToSortMap = new HashMap<>();
        /*for (Map.Entry<k,v> m : entryList){
            System.out.println(m.getKey()+"===>"+m.getValue());
        }*/
        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<JSONObject>();
        jsonArray.forEach(obj ->{
            objectList.add((JSONObject)obj);
        });
        Collections.sort(objectList, new Comparator<JSONObject>() {
            @Override
            public int compare(JSONObject o1, JSONObject 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","麦兜");
        map.put("key3","贝塔");
        map.put("key5","酥妮");
        map.put("key2","小H");
        map.put("key4","小O");
        return map;
    }
    /**
     *准备json测试数据
     */
    public static JSONArray getJsonArrayData(){
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject1 = new JSONObject();
        jsonObject1.put("userId","1001");
        jsonObject1.put("name","麦兜");
        jsonArray.add(jsonObject1);

        JSONObject jsonObject3 = new JSONObject();
        jsonObject3.put("userId","1003");
        jsonObject3.put("name","酥妮");
        jsonArray.add(jsonObject3);

        JSONObject jsonObject2 = new JSONObject();
        jsonObject2.put("userId","1002");
        jsonObject2.put("name","贝塔");
        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对比工具,java,json,开发语言,测试工具

其他

原始代码中有些小bug,已修复。目前这个工具主要被我拿来用在了一个接口数据对比工具中,来检测迭代前后的接口协议数据变更,以完善迭代变更范围来确认测试范围文章来源地址https://www.toymoban.com/news/detail-539396.html

到了这里,关于JSON数据的差异对比工具(Java版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • java hutool工具类处理json的常用方法

    Hutool 提供了丰富的 JSON 处理工具类,包括 JSON 字符串的解析、生成、对象与 JSON 字符串的转换等。以下是 Hutool 中关于 JSON 的常用方法: JSON 字符串的解析与生成: JSONUtil.parseObj(jsonStr) :将 JSON 字符串解析为 JSONObject 对象。 JSONUtil.parseArray(jsonStr) :将 JSON 字符串解析为 JSON

    2024年04月17日
    浏览(10)
  • Java 工具类之JSON key根据ASCII排序

    参数按照KEY值进行字典序排序(按照KEY值的ASCII码从小到大),并用作为各参数之间的分隔符将参数拼接成字符串。这里用到了SortedMap,复制以下代码开箱即用~

    2024年02月12日
    浏览(8)
  • Java工具类——json字符串格式化处理

    Java工具类——json字符串格式化处理

    在我们拿到一团未经格式化的json字符串时,非常不方便查看,比如这样 因此随手写了个工具类用来格式化json。注意,原json字符串必须语法无误,并且不包含换行、空格、缩进等,否则会保留下来。 ok废话不多说上代码 运行后效果

    2024年01月17日
    浏览(17)
  • mysql 5.7 json 类型 json 数组类型 普通字符串类型 10w数据 查询速度差异

    mysql 5.7 json 类型 json 数组类型 普通字符串类型 10w数据 查询速度差异

    建表语句ddl 10w 数据 插入 存储过程  json 类型 vs 普通字符串类型 建表语句ddl CREATE TABLE tb_json_array_test ( id INT NOT NULL AUTO_INCREMENT, user_no VARCHAR(100), user_name VARCHAR(100), score INT, create_time date, update_time date, remark VARCHAR(100), field1 VARCHAR(100), field2 VARCHAR(100), field3 VARCHAR(100), field4 VARCHAR(

    2024年02月04日
    浏览(11)
  • 用反射实现自定义Java对象转化为json工具类

    用反射实现自定义Java对象转化为json工具类

    传入一个object类型的对象 获取该对象的class类 getFields方法获取该类的所有属性 对属性进行遍历,并且拼接成Json格式的字符串,注意:通过属性名来推断方法名 获取Method实例 通过invoke方法调用 以User类进行测试   

    2024年02月11日
    浏览(11)
  • Java后端开发——Ajax、jQuery和JSON

    Java后端开发——Ajax、jQuery和JSON

    Ajax全称是Asynchronous Javascript and XML,即异步的JavaScript和 XML。Ajax是一种Web应用技术,该技术是在JavaScript、DOM、服务器配合下,实现浏览器向服务器发送异步请求。 Ajax异步请求方式不向服务器发出请求,会得到数据后再更新页面(通过DOM操作修改页面内容),整个过程不会发

    2024年02月03日
    浏览(14)
  • 如何使用Java处理JSON数据?

    在Java中,您可以使用许多库来处理JSON数据。以下是使用一种常见的库 Gson 的示例: 首先,确保您已经将 Gson 库添加到您的项目中。您可以在 Maven 中添加以下依赖项: 导入 Gson 类: 创建一个 Gson 实例: 将 JSON 字符串转换为 Java 对象,使用  fromJson()  方法: 上面代码中,将

    2024年02月15日
    浏览(12)
  • Java&Vue 借助json传递数据

    2024年04月26日
    浏览(10)
  • java 获取request中json数据

    java 获取request中json数据

    功能简介:         Java 从 HttpServletRequest 中获取前端传输过来的json数据 效果展示: 请求示例: post: 127.0.0.1:8081/getRequestJson 代码示例: 拓展:  

    2024年02月12日
    浏览(9)
  • Java开发者的Python进修指南:JSON利器之官方json库、demjson和orjson的实用指南

    JSON作为目前最流行的传输格式,在Python中也有相应的实现方式。由于JSON格式的文本可以跨平台并且简单易用,因此被广泛传播。因此,我们今天的主要讨论内容是如何熟练地应用Python的JSON库来处理将JSON映射到文本,以及如何从文本映射到对象中。现在,让我们开始探讨这个

    2024年01月23日
    浏览(9)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包