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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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&…