当前位置: 首页 > 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.lryc.cn/news/601880.html

相关文章:

  • 移植pbrt中的并行化到ray trace in weeks中
  • LangGraph底层API入门总结
  • OpenLayers 综合案例-地图绘制
  • 十字链表以及实现
  • SpringAI入门及浅实践,实战 Spring‎ AI 调用大模型、提示词工程、对话记忆、Adv‎isor 的使用
  • 第五章 中央处理器(CPU)知识体系与考法总结
  • 【第六节】方法与事件处理器
  • Gradle#Plugin
  • Windows---动态链接库Dynamic Link Library(.dll)
  • 2025.7.27总结—新励成
  • Kubernetes 核心组件解析
  • HCIE学习之路:MSTP实现负载均衡实验
  • 【INT范围提取字符串数字为正数】2022-8-29
  • Leetcode 3628. Maximum Number of Subsequences After One Inserting
  • rust- 定义模块以控制作用域和隐私
  • 握手未来,PostgreSQL认证专家
  • 【I】题目解析
  • Spring AI 学习笔记
  • 小架构step系列27:Hibernate提供的validator
  • 「mysql」Mac osx彻底删除mysql
  • Java面试宝典:MySQL性能优化
  • uart通信
  • JVM类加载机制全流程详解
  • 从MySQL的information_schema系统数据库中获取表的元数据信息
  • MySQL - 索引(B+树)
  • Cgroup 控制组学习(三)在容器中使用 CGroups
  • MySQL - 主从复制与读写分离
  • Cline与Cursor深度实战指南:AI编程助手的革命性应用
  • 基于CNN图像特征提取流程(简化版)
  • Linux实战:从零搭建基于LNMP+NFS+DNS的WordPress博客系统