比較兩個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.pswp.cn/web/12502.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12502.shtml
英文地址,請注明出處:http://en.pswp.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…