比较两个JSON之间的差异

网上找到的比较JSON工具类,比较两个JSON对象之间的差异,并将差异字段按照原JSON对象的树状结构展现出来,方便对数据进行对比。对原有方法进行了部分优化。

package com.summer.toolkit.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;import java.util.*;/*** @author author*/
@Slf4j
public class CompareJsonUtils {/*** 正则表达式,匹配数组下标,如:[0]、[1]*/public static final String PATTERN = "\\[[0-9]+\\]";/*** 分隔符*/public static final String REGEX = "\\.";/*** 连接符*/public static final String CONNECTOR = ".";/*** 旧的数据Key值*/public static final String OLD_VALUE_KEY = "oldValue";/*** 新的数据Key值*/public static final String NEW_VALUE_KEY = "newValue";/*** 将两个JSON对象进行比较** @param oldJsonStr 旧的JSON字符串* @param newJsonStr 新的JSON字符串* @return 比较结果转换为JSON字符串*/public static String compareJsonObject(String oldJsonStr, String newJsonStr) {// 默认不排除字段List<String> excludes = new ArrayList<>();return CompareJsonUtils.compareJsonObject(oldJsonStr, newJsonStr, excludes);}/*** 比较两个JSON对象,排除指定字段后的差异** @param oldJsonStr 旧的JSON字符串* @param newJsonStr 新的JSON字符串* @param excludes   需要排除比较的字段列表* @return 返回排除指定字段后的差异JSON对象*/public static String compareJsonObject(String oldJsonStr, String newJsonStr, List<String> excludes) {// 将字符串转换为json对象JSON oldJson = JSON.parseObject(oldJsonStr);JSON newJson = JSON.parseObject(newJsonStr);// 递归遍历json对象所有的key-value,将其封装成path:value格式进行比较Map<String, Object> oldMap = new LinkedHashMap<>(32);Map<String, Object> newMap = new LinkedHashMap<>(32);CompareJsonUtils.convertJsonToMap(oldJson, "", oldMap);CompareJsonUtils.convertJsonToMap(newJson, "", newMap);// 去掉不需要对比的字段CompareJsonUtils.excludeFields(oldMap, excludes);CompareJsonUtils.excludeFields(newMap, excludes);// 比较两个map,返回不同数据Map<String, Object> temp = new LinkedHashMap<>(oldMap);Map<String, Object> differenceMap = CompareJsonUtils.compareMap(temp, newMap);// 将最终的比较结果把不相同的转换为json对象返回JSONObject result = CompareJsonUtils.convertMapToJson(differenceMap);log.info("对比JSON结果为:{}", result);return JSON.toJSONString(result);}/*** 从给定的Map中排除指定的字段** @param map      包含字段的Map* @param excludes 要排除的字段列表*/public static void excludeFields(Map<String, Object> map, List<String> excludes) {if (Objects.nonNull(map) && CollectionUtils.isNotEmpty(excludes)) {Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, Object> entry = iterator.next();if (Objects.nonNull(entry)) {if (excludes.contains(entry.getKey())) {iterator.remove();}}}}}/*** 将json数据转换为map存储用于比较** @param json      JSON对象* @param root      根对象名称* @param resultMap 结果*/private static void convertJsonToMap(Object json, String root, Map<String, Object> resultMap) {if (json instanceof JSONObject) {JSONObject jsonObject = ((JSONObject) json);for (String key : jsonObject.keySet()) {Object value = jsonObject.get(key);String newRoot = "".equals(root) ? key + "" : root + CompareJsonUtils.CONNECTOR + key;if (value instanceof JSONObject || value instanceof JSONArray) {CompareJsonUtils.convertJsonToMap(value, newRoot, resultMap);} else {resultMap.put(newRoot, value);}}} else if (json instanceof JSONArray) {JSONArray jsonArray = (JSONArray) json;for (int i = 0; i < jsonArray.size(); i++) {Object value = jsonArray.get(i);String newRoot = "".equals(root) ? "[" + i + "]" : root + ".[" + i + "]";if (value instanceof JSONObject || value instanceof JSONArray) {CompareJsonUtils.convertJsonToMap(value, newRoot, resultMap);} else {resultMap.put(newRoot, value);}}}}/*** 比较两个map,返回不同数据** @param oldMap 旧数据MAP对象* @param newMap 新数据MAP对象* @return 返回值,两者的差异*/private static Map<String, Object> compareMap(Map<String, Object> oldMap, Map<String, Object> newMap) {// 遍历newMap,将newMap的不同数据装进oldMap,同时删除oldMap中与newMap相同的数据CompareJsonUtils.compareNewToOld(oldMap, newMap);// 将旧的有新的没有的数据封装数据结构存在旧的里面CompareJsonUtils.compareOldToNew(oldMap);return oldMap;}/*** 将旧的有新的没有的数据封装数据结构存在旧的里面** @param oldMap 旧数据结构*/private static void compareOldToNew(Map<String, Object> oldMap) {// 统一oldMap中newMap不存在的数据的数据结构,便于解析for (Map.Entry<String, Object> item : oldMap.entrySet()) {String key = item.getKey();Object value = item.getValue();if (!(value instanceof Map)) {// 此处并没有添加数据,而是将key值对应的value值进行修改,所以并没有改变map的数量Map<String, Object> differenceMap = CompareJsonUtils.buildDifferenceMap(value, "");oldMap.put(key, differenceMap);}}}/*** 将新的map与旧的比较,并将数据统一存在旧的里面** @param oldMap 旧数据* @param newMap 新数据*/private static void compareNewToOld(Map<String, Object> oldMap, Map<String, Object> newMap) {for (Map.Entry<String, Object> item : newMap.entrySet()) {String key = item.getKey();Object newValue = item.getValue();if (oldMap.containsKey(key)) {Object oldValue = oldMap.get(key);if (newValue.equals(oldValue)) {oldMap.remove(key);} else {Map<String, Object> differenceMap = CompareJsonUtils.buildDifferenceMap(oldValue, newValue);oldMap.put(key, differenceMap);}} else {Map<String, Object> differenceMap = CompareJsonUtils.buildDifferenceMap("", newValue);oldMap.put(key, differenceMap);}}}/*** 将已经找出不同数据的map根据key的层级结构封装成json返回** @param map 入参* @return 返回值*/private static JSONObject convertMapToJson(Map<String, Object> map) {JSONObject resultJsonObject = new JSONObject();for (Map.Entry<String, Object> item : map.entrySet()) {String key = item.getKey();Object value = item.getValue();String[] paths = key.split(CompareJsonUtils.REGEX);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(CompareJsonUtils.PATTERN)) {remarkObject = new JSONArray();} else {remarkObject = new JSONObject();}resultJsonObject.put(path, remarkObject);} else {resultJsonObject.put(path, value);}}i++;continue;}// 匹配集合对象if (path.matches(CompareJsonUtils.PATTERN)) {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(CompareJsonUtils.PATTERN)) {while (((JSONArray) remarkObject).size() <= index) {if (((JSONArray) remarkObject).size() == index) {((JSONArray) remarkObject).add(index, new JSONArray());} else {//((JSONArray) remarkObject).add(null);((JSONArray) remarkObject).add(new JSONObject());}}} else {while (((JSONArray) remarkObject).size() <= index) {if (((JSONArray) remarkObject).size() == index) {((JSONArray) remarkObject).add(index, new JSONObject());} else {//((JSONArray) remarkObject).add(null);((JSONArray) remarkObject).add(new JSONObject());}}}remarkObject = ((JSONArray) remarkObject).get(index);} else {while (Objects.nonNull(remarkObject) && ((JSONArray) remarkObject).size() <= index) {if (((JSONArray) remarkObject).size() == index) {((JSONArray) remarkObject).add(index, value);} else {//((JSONArray) remarkObject).add(null);((JSONArray) remarkObject).add(new JSONObject());}}}} else {if (indexAll > i) {if (paths[i + 1].matches(CompareJsonUtils.PATTERN)) {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 if (Objects.nonNull(remarkObject)) {((JSONObject) remarkObject).put(path, value);}}i++;}}return resultJsonObject;}/*** 构建差异Map,用于比较旧值和新值之间的差异** @param oldValue 旧值* @param newValue 新值* @return differenceMap 差异Map,包含'oldValue'和'newValue'两个键值对*/public static Map<String, Object> buildDifferenceMap(Object oldValue, Object newValue) {Map<String, Object> differenceMap = new HashMap<>(4);differenceMap.put(CompareJsonUtils.OLD_VALUE_KEY, oldValue);differenceMap.put(CompareJsonUtils.NEW_VALUE_KEY, newValue);return differenceMap;}/*** 比较两个JSON对象并返回它们之间的差异*/public static void main(String[] args) {String oldStr = "{a:'aaa',b:'bbb',c:'aaa',d:'bbb'}";String newStr = "{a:'aa',b:'bb',e:'bb'}";System.out.println(CompareJsonUtils.compareJsonObject(oldStr, newStr));}}

以上测试方法结果为:

{"b": {"newValue": "bb","oldValue": "bbb"},"c": {"newValue": "","oldValue": "aaa"},"d": {"newValue": "","oldValue": "bbb"},"e": {"newValue": "bb","oldValue": ""}
}

参考链接:【Java】Java对比两个JSON对象(深度广度比较)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/12502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三、安装node_exporter

目录 一、简介 二、下载安装 一、简介 Exporter是Prometheus的指标数据收集组件。它负责从目标Jobs收集数据&#xff0c;并把收集到的数据转换为Prometheus支持的时序数据格式。 和传统的指标数据收集组件不同的是&#xff0c;他只负责收集&#xff0c;并不向Server端发送数据…

探索微软Edge:一款重塑网页浏览体验的新锐浏览器

探索微软Edge&#xff1a;一款重塑网页浏览体验的新锐浏览器 随着科技的飞速发展&#xff0c;我们的互联网浏览需求也在不断升级。在这样的背景下&#xff0c;微软Edge浏览器应运而生&#xff0c;以其卓越的性能、独特的功能和简洁的设计&#xff0c;迅速赢得了广大用户的青睐…

Redis:常用数据结构

文章目录 常用数据结构Redis的编码方式查看方式 常用数据结构 Redis当中常用的数据结构如下所示&#xff1a; Redis在底层实现上述数据结构的过程中&#xff0c;会在源码的角度上对于上述的内容进行特定的优化&#xff0c;这样的优化的主要目的是为了实现出节省时间和节省空间…

【挑战30天首通《谷粒商城》】-【第一天】10、环境-docker安装mysql

文章目录 课程介绍一、docker 安装 mysql Stage 1&#xff1a;下载镜像文件 Stage 1-1&#xff1a;打开官网查看镜像 Stage 1-2&#xff1a;拉取镜像 Stage 1-3&#xff1a;查看拉取的镜像 Stage 2&#xff1a;创建实例并启动 A&#xff1a;mysql&#xff08;5.7版&#xff09;…

yolov8添加FPPI评价指标

这里写自定义目录标题 yolov8 中FPPI实现测试中调用 效果结语 续yolov7添加FPPI评价指标 。之前在yolov7中增加了fppi指标&#xff0c;有不少网友问有没有yolov8中增加&#xff0c;最近没有做算法训练&#xff0c;也一直没时间弄。这几天晚上抽了点时间&#xff0c;弄了一下。不…

焦灼上市背后,极氪汽车开启新长征?

李书福的资本帝国&#xff0c;又要扩容了。继蔚小理之后&#xff0c;极氪汽车成为第四家赴美上市的中国造车新势力&#xff0c;同时也成为了李书福收获的第九个IPO。作为一家成立仅仅4年的新势力&#xff0c;极氪再次刷新了造车新势力上市的最快记录。 按照极氪汽车官方的说法…

学习中...【京东价格/评论数据】数据获取方式——采用Selenium★

近期闲来无事学学selenium爬虫技术&#xff0c;参考崔庆才《Python3网络爬虫开发实战》的淘宝商品信息爬取&#xff0c;我也照猫画虎的学了京东的价格和商品评论数据。废话不多说&#xff0c;直接开始吧&#xff01; 1. 浏览器初始化 from selenium import webdriver from se…

红黑树的平衡

1.红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c…

什么是apt

2024年5月15日&#xff0c;周三上午 apt 是 “Advanced Packaging Tool” 的缩写&#xff0c;它是 Debian 及其衍生版&#xff08;如 Ubuntu&#xff09;中用于管理软件包的命令行工具。apt 提供了一个统一的接口来安装、更新、升级、删除和搜索软件包。 以下是 apt 的一些主要…

合合信息:TextIn文档解析技术与高精度文本向量化模型再加速

文章目录 前言现有大模型文档解析问题表格无法解析无法按照阅读顺序解析文档编码错误 诉求文档解析技术技术难点技术架构关键技术回根溯源 文本向量化模型结语 前言 随着人工智能技术的持续演进&#xff0c;大语言模型在我们日常生活中正逐渐占据举足轻重的地位。大模型语言通…

Java基础(36)应用服务器优化技术有哪些

应用服务器优化是一个复杂的过程&#xff0c;涉及到服务器硬件资源、操作系统、网络、应用程序代码、数据库等多个层面。下面是一些深入详细的应用服务器优化技术&#xff1a; 系统级优化 硬件优化 提升CPU性能&#xff1a;使用更多核心的CPU或者升级到更高频率的CPU。增加内…

Scala基础

目录 1.安装与运行Scala 任务描述 了解Scala语言 了解Scala特性 安装Scala 运行Scala 2.定义函数识别号码类型 了解数据类型 定义与使用常量、变量 使用运算符 定义与使用数组 任务实现 3.基本语法 1 变量 2 字符串 3 数据类型&操作符 4 条件表达式 5 循环…

idea使用gitee基本操作流程

1.首先&#xff0c;每次要写代码前&#xff0c;先切换到自己负责的分支 点击签出。 然后拉取一次远程master分支&#xff0c;保证得到的是最新的代码。 写完代码后&#xff0c;在左侧栏有提交按钮。 点击后&#xff0c;选择更新的文件&#xff0c;输入描述内容&#xff08;必填…

五分钟“手撕”时间复杂度与空间复杂度

目录 一、算法效率 什么是算法 如何衡量一个算法的好坏 算法效率 二、时间复杂度 时间复杂度的概念 大O的渐进表示法 推导大O阶方法 常见时间复杂度计算举例 三、空间复杂度 常见时间复杂度计算举例 一、算法效率 什么是算法 算法(Algorithm)&#xff1a;就是定…

C++|多态性与虚函数(2)|虚析构函数|重载函数|纯虚函数|抽象类

前言 看这篇之前&#xff0c;可以先看多态性与虚函数&#xff08;1&#xff09;⬇️ C|多态性与虚函数&#xff08;1&#xff09;功能绑定|向上转换类型|虚函数-CSDN博客https://blog.csdn.net/weixin_74197067/article/details/138861418?spm1001.2014.3001.5501这篇文章会…

【Java开发面试系列】JVM相关面试题(精选)

【Java开发面试系列】JVM相关面试题&#xff08;精选&#xff09; 文章目录 【Java开发面试系列】JVM相关面试题&#xff08;精选&#xff09;前言一、JVM组成二、类加载器三、垃圾回收四、JVM实践&#xff08;调优&#xff09; &#x1f308;你好呀&#xff01;我是 山顶风景独…

【十大排序算法】----C语言版插入排序(详细图解)

目录 一&#xff1a;插入排序——原理 二&#xff1a;插入排序——分析 三&#xff1a;插入排序——实现 四&#xff1a;插入排序——效率 一&#xff1a;插入排序——原理 插入排序的原理和基本思想&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序…

无需重启 NGINX 开源版即可实现 SSL/TLS 证书轮换

原文作者&#xff1a;Maxim Ivanitskiy of F5 原文链接&#xff1a;无需重启 NGINX 开源版即可实现 SSL/TLS 证书轮换 转载来源&#xff1a;NGINX 开源社区 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn 在高性能 Web 服务器领域&#xff0c;NGINX 是一个广受欢迎的…

Django使用

一、根目录下安装 pip install django 二、创建djiango项目 django-admin startproject 项目名称 三、创建app python manage.py startapp app名称 四、启动 python manage.py runserver 五、编写URL与视图关系&#xff0c;相对路径 1、manage.py&#xff08;见资源绑定…

mysql 8 创建用户并赋予改用户指定数据库权限

一、使用客户端工具登录mysql 二、创建用户 -- 低版本数据库 create user 用户名% identified by 密码; -- 高版本数据库 create user 用户名% identified with mysql_native_password by 密码; -- 示例1&#xff1a; create user test% identified with mysql_native_passwor…