当前位置: 首页 > news >正文

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.dtcms.com/a/300843.html

相关文章:

  • 2025年量子计算与前沿技术融合:六大变革性方向深度解析
  • Rust实战:高效开发技巧
  • 02人工智能中优雅草商业实战项目视频字幕翻译以及声音转译之以三方AI模型API制作方式预算-卓伊凡|莉莉
  • 【在Unity游戏开发中Dictionary、List介绍】
  • 基于Springboot+UniApp+Ai实现模拟面试小工具七:前端项目创建及框架搭建
  • 深入理解 Spring 中的 XmlBeanFactory 原理及实践
  • 【最新版】防伪溯源一体化管理系统+uniapp前端+搭建教程
  • ArKTS:List 数组
  • 机器学习特征选择 explanation and illustration of ANOVA
  • ROS2总结(二)
  • UDS 0x29 身份验证服务 Authentication service
  • Rust Web 全栈开发(十一):WebAssembly 尝鲜
  • 2507rust,rust写驱动
  • rust- 定义模块以控制作用域和隐私
  • 无刷电机三项霍尔连接线序组合详细分析与波形实例
  • ETF历史每笔成交分钟级高频数据深度解析
  • 墨者:通过手工解决SQL手工注入漏洞测试(MongoDB数据库)
  • Rust与Java DynamoDB、MySQL CRM、tokio-pg、SVM、Custors实战指南
  • 零基础 “入坑” Java--- 十四、字符串String
  • mybatis-plus实体类主键生成策略
  • 使用uni-app开发一个点餐收银台系统前端静态项目练习
  • 车辆网络安全规定之R155与ISO/SAE 21434
  • 09_opencv_遍历操作图像像素
  • uniapp input 聚焦时键盘弹起滚动到对应的部分
  • 基础配置介绍,VLAN配置,DHCP配置
  • 迷宫生成与路径搜索(A算法可视化)
  • SparkSQL — get_json_object函数详解(解析 json)
  • 离散组合数学 : 母函数
  • QT6 源,七章对话框与多窗体(16)多文档 MDI 窗体 QMdiArea 篇二:源代码带注释
  • 栈----4.每日温度