JSON格式化與結構對比

說明

功能

  1. 格式化json字符串為最簡格式,并標識值類型;

  2. 比對json字符串結構。

第三方依賴

  1. fastjson: 用于解析json、判斷json值類型;

  2. springframework自帶的字符串判斷,可以不依賴該方法,改為自行實現;

  3. slf4j: 用于打印日志,可以不依賴該方法,改為其它方法。

json結構對比規則

  1. null與任何類型相等;

  2. 空對象{}與任何對象{}相等;

  3. 空數組[]與任何數組[]相等。


代碼

JSON工具類

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;import java.util.Map;
import java.util.Set;@Slf4j
public class JSONUtil {private static final String NULL = "Null";private static final String OBJECT = "Object";private static final String ARRAY = "Array";private static final String EQUAL = "=";private static final String ADD = "+";private static final String DELETE = "-";private static final String MODIFY = "%s -> %s";private static final String CAN_NOT_COMPARE = "not json, can't compare!";private static final String CAN_NOT_FORMAT = "not json, can't format!";/*** 格式化json字符串為最簡格式,并標識值類型** @param json json字符串* @return json結構*/public static String format(String json) {if (!StringUtils.hasText(json)) {return CAN_NOT_FORMAT;}try {Object formatJson = null;if (json.trim().startsWith("{")) {formatJson = JSON.parseObject(json);formatJSONObject((JSONObject) formatJson);} else if (json.trim().startsWith("[")) {formatJson = JSON.parseArray(json);formatJSONArray((JSONArray) formatJson);}return JSON.toJSONString(formatJson);} catch (JSONException exception) {log.warn("compareStruct error", exception);}return CAN_NOT_FORMAT;}private static Object formatJSONObject(JSONObject json) {if (json == null) {return null;}if (json.isEmpty()) {return OBJECT;}for (Map.Entry<String, Object> entry : json.entrySet()) {Object value = entry.getValue();if (value instanceof JSONObject) {entry.setValue(formatJSONObject((JSONObject) value));} else if (value instanceof JSONArray) {entry.setValue(formatJSONArray((JSONArray) value));} else {entry.setValue(getTypeName(value));}}return json;}private static Object formatJSONArray(JSONArray json) {if (json == null) {return null;}if (json.isEmpty()) {return ARRAY;}Object typical = json.get(0);if (typical instanceof JSONObject) {typical = formatJSONObject((JSONObject) typical);} else if (typical instanceof JSONArray) {typical = formatJSONArray((JSONArray) typical);} else {typical = getTypeName(typical);}json.clear();json.add(typical);return json;}/*** 比對json字符串* <p>* 說明:* 1、null與任何類型相等;* 2、{}與任何{}相等;* 3、[]與任何[]相等;** @param oldJson 舊json字符串* @param newJson 新json字符串* @return 新舊json字符串差異*/public static Object compareStruct(String oldJson, String newJson) {if (!StringUtils.hasText(oldJson) || !StringUtils.hasText(newJson)) {return CAN_NOT_COMPARE;}try {if (oldJson.trim().startsWith("{")) {if (newJson.trim().startsWith("{")) {JSONObject oldJsonObject = JSON.parseObject(oldJson);JSONObject newJsonObject = JSON.parseObject(newJson);if (oldJsonObject == null || newJsonObject == null || oldJsonObject.isEmpty() || newJsonObject.isEmpty()) {// null與任何類型相等;{}與任何{}相等return EQUAL;}JSONObject result = new JSONObject();compareJSONObject(oldJsonObject, newJsonObject, result);return result;} else {return String.format(MODIFY, OBJECT, ARRAY);}} else if (oldJson.trim().startsWith("[")) {if (newJson.trim().startsWith("[")) {JSONArray oldJsonArray = JSON.parseArray(oldJson);JSONArray newJsonArray = JSON.parseArray(newJson);if (oldJsonArray == null || newJsonArray == null || oldJsonArray.isEmpty() || newJsonArray.isEmpty()) {// null與任何類型相等;[]與任何[]相等return EQUAL;}JSONArray result = new JSONArray();compareJSONArray(oldJsonArray, newJsonArray, result);if (result.size() == 1 && EQUAL.equals(result.get(0))) {return EQUAL;} else {return result;}} else {return String.format(MODIFY, ARRAY, OBJECT);}}} catch (JSONException exception) {log.warn("compareStruct error", exception);}return CAN_NOT_COMPARE;}private static void compareJSONObject(JSONObject oldJson, JSONObject newJson, JSONObject result) {if (oldJson == null || newJson == null) {// 該空校驗可以去掉,調用的地方已經校驗過了return;}Set<String> oldKeySet = oldJson.keySet();Set<String> newKeySet = newJson.keySet();for (Map.Entry<String, Object> entry : newJson.entrySet()) {if (!oldKeySet.contains(entry.getKey())) {result.put(entry.getKey(), ADD);continue;}Object newValue = entry.getValue();Object oldValue = oldJson.get(entry.getKey());if (oldValue == null || newValue == null) {result.put(entry.getKey(), EQUAL);continue;}if (!newValue.getClass().equals(oldValue.getClass())) {result.put(entry.getKey(), String.format(MODIFY, getTypeName(oldValue), getTypeName(newValue)));continue;}if (newValue instanceof JSONObject) {JSONObject oldValueJson = (JSONObject) oldValue;JSONObject newValueJson = (JSONObject) newValue;if (oldValueJson.isEmpty() || newValueJson.isEmpty()) {result.put(entry.getKey(), EQUAL);continue;}JSONObject subResult = new JSONObject();result.put(entry.getKey(), subResult);compareJSONObject(oldValueJson, newValueJson, subResult);} else if (newValue instanceof JSONArray) {JSONArray subResult = new JSONArray();compareJSONArray((JSONArray) oldValue, (JSONArray) newValue, subResult);if (subResult.size() == 1 && EQUAL.equals(subResult.get(0))) {// 嵌套數組,如果內層結構相同,那么本層結構也相同result.put(entry.getKey(), EQUAL);} else {result.put(entry.getKey(), subResult);}} else {result.put(entry.getKey(), EQUAL);}}for (Map.Entry<String, Object> entry : oldJson.entrySet()) {if (!newKeySet.contains(entry.getKey())) {result.put(entry.getKey(), DELETE);}}}private static void compareJSONArray(JSONArray oldJson, JSONArray newJson, JSONArray result) {if (oldJson == null || newJson == null || oldJson.isEmpty() || newJson.isEmpty()) {result.add(EQUAL);return;}// 取第一個元素對比Object oldTypical = oldJson.get(0);Object newTypical = newJson.get(0);if (oldTypical == null || newTypical == null) {result.add(EQUAL);return;}if (!newTypical.getClass().equals(oldTypical.getClass())) {result.add(String.format(MODIFY, getTypeName(oldTypical), getTypeName(newTypical)));return;}if (newTypical instanceof JSONObject) {JSONObject subResult = new JSONObject();result.add(subResult);compareJSONObject((JSONObject) oldTypical, (JSONObject) newTypical, subResult);} else if (newTypical instanceof JSONArray) {JSONArray subResult = new JSONArray();compareJSONArray((JSONArray) oldTypical, (JSONArray) newTypical, subResult);if (subResult.size() == 1 && EQUAL.equals(subResult.get(0))) {// 嵌套數組,如果內層結構相同,那么本層結構也相同result.add(EQUAL);} else {result.add(subResult);}} else {result.add(EQUAL);}}private static Object getTypeName(Object obj) {if (obj == null) {return NULL;}if (obj instanceof JSONObject) {return OBJECT;}if (obj instanceof JSONArray) {return ARRAY;}return obj.getClass().getSimpleName();}
}

測試

測試代碼

import com.alibaba.fastjson.JSON;
import com.example.study.util.JSONUtil;public class Test {public static void main(String[] args) {System.out.println(JSON.toJSONString(JSONUtil.compareStruct("{}", "{}")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[]", "[]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("{}", "[]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[]", "{}")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[]", null)));System.out.println(JSON.toJSONString(JSONUtil.compareStruct(null, "{}")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[]", "")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("", "{}")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[[]]", "[[]]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[[2]]", "[[1]]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[[]]", "[[[]]]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[[]]", "[[[1]]]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[[1]]", "[[[1]]]")));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[", "[[[1]]]")));String oldJsonObj = "{\"id\":1,\"isMan\":true,\"name\":\"testName\",\"testNull\":null,\"testEmptyObject\":{}," +"\"testObject\":{\"id\":1,\"arr\":[]},\"testEmptyArr\":[],\"testIntegerArr\":[1,2,3],\"testObjectArr\":[{\"id\":1,\"arr\":[\"a\"]},{\"id\":2,\"arr\":[\"b\"]}],\"testNestingArr\":[[[1,2,3]]],\"testNestingArrEqual\":[[[\"1\"]]]}";String newJsonObj = "{\"id\":\"1\",\"isMan\":true,\"name\":\"testName\",\"testNull\":{}," +"\"testEmptyObject\":{},\"testObject\":{\"id\":1,\"testAdd\":\"add\",\"arr\":[]},\"testEmptyArr\":[],\"testIntegerArr\":[1,2,3],\"testObjectArr\":[{\"arr\":[\"a\",\"b\",\"c\",\"d\"]},{\"arr\":[\"b\",\"b\",\"c\",\"d\"]}],\"testNestingArr\":[[[\"a\",\"b\",\"c\",\"d\"]]],\"testNestingArrEqual\":[[[\"a\",\"b\",\"c\",\"d\"]]]}";System.out.println(JSON.toJSONString(JSONUtil.compareStruct(oldJsonObj, newJsonObj)));System.out.println(JSON.toJSONString(JSONUtil.compareStruct("[" + oldJsonObj + "]", "[" + newJsonObj + "]")));oldJsonObj = "{\"id\":1,\"isMan\":true,\"name\":\"testName\",\"testNull\":null,\"testEmptyObject\":{}," +"\"testObject\":{\"id\":1,\"arr\":[]},\"testEmptyArr\":[],\"testIntegerArr\":[1,2,3],\"testObjectArr\":[{\"id\":1,\"arr\":[\"a\"]},{\"id\":2,\"arr\":[\"b\"]}],\"testNestingArr\":[[[1,2,3]]],\"testNestingArrEqual\":[[[\"1\"]]],\"nullArr0\":[null],\"nullArr1\":[[[null]]],\"emptyArr0\":[],\"emptyArr1\":[[[]]],\"nestingArr0\":[[[1,2,3]]],\"nestingArr1\":[[[\"a\"]]],\"nestingArr2\":[[[{\"id\":2,\"arr\":[\"b\"],\"arr2\":[[]]}]]]}";System.out.println(JSON.toJSONString(JSONUtil.compareStruct(oldJsonObj, newJsonObj)));System.out.println(JSONUtil.format(oldJsonObj));}
}

輸出

"="
"="
"Object -> Array"
"Array -> Object"
"not json, can't compare!"
"not json, can't compare!"
"not json, can't compare!"
"not json, can't compare!"
"="
"="
"="
"="
[["Integer -> Array"]]
18:08:25.205 [main] WARN com.example.study.util.JSONUtil -- compareStruct error
com.alibaba.fastjson.JSONException: unclosed jsonArrayat com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:1266)at com.alibaba.fastjson.parser.DefaultJSONParser.parseArray(DefaultJSONParser.java:1169)at com.alibaba.fastjson.JSON.parseArray(JSON.java:612)at com.alibaba.fastjson.JSON.parseArray(JSON.java:592)at com.example.study.util.JSONUtil.compareStruct(JSONUtil.java:124)at com.example.study.controller.Test.main(Test.java:21)
"not json, can't compare!"
{"testObjectArr":[{"arr":"=","id":"-"}],"testObject":{"arr":"=","testAdd":"+","id":"="},"name":"=","testIntegerArr":"=","id":"Integer -> String","testNull":"=","testEmptyObject":"=","testNestingArrEqual":"=","isMan":"=","testEmptyArr":"=","testNestingArr":[[["Integer -> String"]]]}
[{"testObjectArr":[{"arr":"=","id":"-"}],"testObject":{"arr":"=","testAdd":"+","id":"="},"name":"=","testIntegerArr":"=","id":"Integer -> String","testNull":"=","testEmptyObject":"=","testNestingArrEqual":"=","isMan":"=","testEmptyArr":"=","testNestingArr":[[["Integer -> String"]]]}]
{"nullArr0":"-","nullArr1":"-","testIntegerArr":"=","emptyArr0":"-","nestingArr1":"-","emptyArr1":"-","nestingArr2":"-","testNestingArrEqual":"=","nestingArr0":"-","isMan":"=","testEmptyArr":"=","testNestingArr":[[["Integer -> String"]]],"testObjectArr":[{"arr":"=","id":"-"}],"testObject":{"arr":"=","testAdd":"+","id":"="},"name":"=","id":"Integer -> String","testNull":"=","testEmptyObject":"="}
{"nullArr0":["Null"],"nullArr1":[[["Null"]]],"testIntegerArr":["Integer"],"emptyArr0":"Array","nestingArr1":[[["String"]]],"emptyArr1":[["Array"]],"nestingArr2":[[[{"arr":["String"],"id":"Integer","arr2":["Array"]}]]],"testNestingArrEqual":[[["String"]]],"nestingArr0":[[["Integer"]]],"isMan":"Boolean","testEmptyArr":"Array","testNestingArr":[[["Integer"]]],"testObjectArr":[{"arr":["String"],"id":"Integer"}],"testObject":{"arr":"Array","id":"Integer"},"name":"String","id":"Integer","testNull":"Null","testEmptyObject":"Object"}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90778.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90778.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90778.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

編程與數學 03-002 計算機網絡 03_物理層基礎

編程與數學 03-002 計算機網絡 03_物理層基礎一、物理層的作用與任務&#xff08;一&#xff09;傳輸媒體的類型&#xff08;二&#xff09;信號的傳輸方式二、數據編碼技術&#xff08;一&#xff09;數字數據的數字信號編碼&#xff08;二&#xff09;模擬數據的數字信號編碼…

c語言--文件操作

思維導圖:1. 為什么使用文件&#xff1f; 如果沒有文件&#xff0c;我們寫的程序的數據是存儲在電腦的內存中&#xff0c;如果程序退出&#xff0c;內存回收&#xff0c;數據就丟失了&#xff0c;等再次運?程序&#xff0c;是看不到上次程序的數據的&#xff0c;如果要將數據進…

SQL中的占位符、@Param注解和方法參數

代碼中出現的多個 username 和 password 代表不同層面的變量&#xff0c;具體含義如下&#xff08;按執行順序&#xff09;&#xff1a;### 1. Param("username") String username - 位置 &#xff1a;方法參數前的注解 - 作用 &#xff1a;- Param("username&q…

【SpringAI實戰】FunctionCalling實現企業級自定義智能客服

一、前言 二、實現效果 三、代碼實現 3.1 后端實現 3.2 前端實現 一、前言 Spring AI詳解&#xff1a;【Spring AI詳解】開啟Java生態的智能應用開發新時代(附不同功能的Spring AI實戰項目)-CSDN博客 二、實現效果 一個24小時在線的AI智能客服&#xff0c;可以給用戶提供培…

kotlin基礎【2】

變量類型var 和 val 的核心區別&#xff1a;關鍵字含義能否重新賦值類似概念&#xff08;Java&#xff09;varvariable&#xff08;可變變量&#xff09;可以普通變量&#xff08;無 final&#xff09;valvalue&#xff08;不可變變量&#xff09;不可以被 final 修飾的變量var…

【Spring AI】阿里云DashScope靈積模型

DashScope&#xff08;靈積模型&#xff09;是阿里云提供的大模型服務平臺&#xff0c;集成了阿里自研的 通義千問&#xff08;Qwen&#xff09;系列大語言模型&#xff08;LLM&#xff09;以及多模態模型&#xff0c;為企業與開發者提供開箱即用的 AI 能力。官網地址 https://…

Rust Web框架性能對比與實戰指南

Rust Actix Web Rust Web 框架的實用對比分析 以下是 Rust Web 框架的實用對比分析,涵蓋主要框架(如 Actix-web、Rocket、Warp、Axum 等)的常見使用場景示例,按功能分類整理: 基礎路由設置 Actix-web use actix_web::{get, App, HttpResponse, HttpServer, Responder}…

【解決vmware ubuntu不小心刪boot分區,進不去系統】

如果仍然提示 Unable to locate package testdisk&#xff0c;有可能是源中不包含該工具&#xff08;LiveCD 使用的是“最小環境”&#xff09;。 &#x1fa9b; 解決方法&#xff1a;切換到國內完整軟件源&#xff08;推薦&#xff09; 編輯 sources.list&#xff1a; sudo na…

04-netty基礎-Reactor三種模型

1 基本概念Reactor模型是一種事件驅動&#xff08;Event-Driven&#xff09;的設計模式&#xff0c;主要用于高效處理高并發、I/O密集型場景&#xff08;如網絡、服務器、分布式等&#xff09;。其核心思想就是集中管理事件&#xff0c;將I/O操作與業務邏輯解耦&#xff0c;避免…

踩坑無數!NFS服務從入門到放棄再到真香的血淚史

前言 說起NFS&#xff0c;我估計很多搞運維的兄弟都有一肚子話要說。這玩意兒吧&#xff0c;看起來簡單&#xff0c;用起來坑多&#xff0c;但是真正搞明白了又覺得挺香的。 前幾天有個朋友問我&#xff0c;說他們公司要搭建一個文件共享系統&#xff0c;問我推薦什么方案。我…

矩陣譜分解的證明及計算示例

1. 矩陣譜分解的條件矩陣的譜分解&#xff08;也稱為特征分解&#xff09;是將一個矩陣分解為一系列由其特征向量和特征值構成的矩陣乘積的過程。進行譜分解的前提條件包括&#xff1a;<1.> 矩陣是可對角化的&#xff08;Diagonalizable&#xff09;&#xff0c;即矩陣存…

Leetcode 07 java

169. 多數元素 給定一個大小為 n 的數組 nums &#xff0c;返回其中的多數元素。 多數元素是指在數組中出現次數 大于 ? n/2 ? 的元素。 你可以假設數組是非空的&#xff0c;并且給定的數組總是存在多數元素。 示例 1&#xff1a; 輸入&#xff1a;nums [3,2,3] 輸出&a…

CS231n-2017 Lecture6訓練神經網絡(一)筆記

本節主要講的是模型訓練時的算法設計數據預處理&#xff1a;關于數據預處理&#xff0c;我們有常用的3個符號&#xff0c;數據矩陣X&#xff0c;假設其尺寸是&#xff0c;N是數據樣本的數量&#xff0c;D是數據的維度均值減法(Mean subtraction)&#xff1a;是預處理最常用的形…

C++ 中實現 `Task::WhenAll` 和 `Task::WhenAny` 的兩種方案

&#x1f4da; C 中實現 Task::WhenAll 和 Task::WhenAny 的兩種方案 引用&#xff1a; 拈朵微笑的花 想一番人世變換 到頭來輸贏又何妨日與夜互消長 富與貴難久長 今早的容顏老於昨晚C 標準庫異步編程示例&#xff08;一&#xff09;C TAP&#xff08;基于任務的異步編程…

【學習】Codeforces Global Round 15 C. Maximize the Intersections

題意&#xff1a;給出一個圓&#xff0c;順時針排布1~2*n&#xff0c;已知連了k條邊&#xff0c;問這個圓最好情況下有多少個線的交點&#xff0c;要求線與線之間不能有重復的連接點&#xff0c;也就是每個點只能被一條線連接 思路&#xff1a; 1.考慮沒有線的時候&#xff0…

圖論:Dijkstra算法

昨天介紹了最小生成樹的兩個算法&#xff0c;最小生成樹的兩個算法旨在求解無向有權圖中的最小代價聯通圖的問題&#xff0c;那么對于有向有權圖&#xff0c;從起點到終點的最小花費代價問題就可以用 Dijkstra 算法來解決而且Dijkstra算法可以求出來從起始點開始到所有節點的最…

WPFC#超市管理系統(2)顧客管理、供應商管理、用戶管理

超市管理系統3. 顧客管理3.1 顧客新增3.2 DataGrid樣式3.3 顧客刪除3.4 顧客修改4. 供應商管理4.1 供應商管理主界面4.2 新增供應商4.3 修改供應商5. 用戶管理5.1 用戶管理主界面5.2 新增用戶5.3 修改用戶總結3. 顧客管理 在CustomerView.xaml使用命令綁定方式添加頁面加載Loa…

Windows本地部署DeepSeek

1、Ollama1、下載Ollama安裝包https://ollama.com/download&#xff08;如果下載很慢 可以直接找我拿安裝包&#xff09;2、使用命令行安裝打開cmd 將下載的安裝包OllamaSetup.exe 放到想要安裝的目錄下。&#xff08;如果直接雙擊&#xff0c;會裝到C盤&#xff09;例如想裝到…

基于Python的新聞爬蟲:實時追蹤行業動態

引言 在信息時代&#xff0c;行業動態瞬息萬變。金融從業者需要實時了解政策變化&#xff0c;科技公司需要跟蹤技術趨勢&#xff0c;市場營銷人員需要掌握競品動向。傳統的人工信息收集方式效率低下&#xff0c;難以滿足實時性需求。Python爬蟲技術為解決這一問題提供了高效方…

阿里視頻直播解決方案VS(MediaMTX + WebRTC) 流媒體解決方案

背景&#xff1a; 公司采購了新的攝像頭&#xff0c;通過rtsp或者rtmp推流到云平臺&#xff0c;云平臺內部進行轉碼處理&#xff0c;客戶端使用HLS或HTTP-FLV播放&#xff0c;移動App可能使用HLS或私有SDK&#xff0c;超低延時則采用WebRTC。 技術選型&#xff1a; RTSP&…