说明
功能
-
格式化json字符串为最简格式,并标识值类型;
-
比对json字符串结构。
第三方依赖
-
fastjson: 用于解析json、判断json值类型;
-
springframework自带的字符串判断,可以不依赖该方法,改为自行实现;
-
slf4j: 用于打印日志,可以不依赖该方法,改为其它方法。
json结构对比规则
-
null与任何类型相等;
-
空对象{}与任何对象{}相等;
-
空数组[]与任何数组[]相等。
代码
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 jsonArray
at 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"}