import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.*;
@Slf4j
public class JsonComparatorUtils {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws IOException {
String jsonBefore = "{"internal": false, "data": {"f8e37381-b7d6-4475-95d3-e763b64306e8": "33", "mw_instanceName": "kkkkkk", "flag": true}, "instanceName": "kkkkkk", "modelId": "104730838641410048", "groupId": "102706472617508864", "templateSearchParam": {"isExportTemp": false}, "type": "instance", "path": ["77170433597636608", "102706472617508864", "104730838641410048"], "itemName": "kkkkkk", "orgIds": [1]}";
String jsonAfter = "{"internal": true, "data": {"f8e37381-b7d6-4475-95d3-e763b64306e8": "33", "mw_instanceName": "llllll", "flag": false}, "instanceName": "kkkkkk", "modelId": "104730838641410048", "groupId": "102706472617508864", "templateSearchParam": {"isExportTemp": false}, "type": "instance", "path": ["77170433597636608", "102706472617508864", "104730838641410048"], "itemName": "kkkkkk", "orgIds": [1]}";
List<String> fieldsToMonitor = Arrays.asList("internal", "instanceName", "mw_instanceName");
JsonNode jsonBeforeNode = mapper.readTree(jsonBefore);
JsonNode jsonAfterNode = mapper.readTree(jsonAfter);
DiffResult diffResult = generateHtmlWithHighlights(jsonBeforeNode, jsonAfterNode);
System.out.println("HTML for json1Before: " + diffResult.getBefore());
System.out.println("HTML for jsonAfter: " + diffResult.getAfter());
}
private static DiffResult generateHtmlWithHighlights(JsonNode jsonBeforeNode, JsonNode jsonAfterNode, List<String> fieldsToMonitor) {
String jsonBefore = highlightDifference(jsonBeforeNode, jsonAfterNode, fieldsToMonitor);
String jsonAfter = highlightDifference(jsonAfterNode, jsonBeforeNode, fieldsToMonitor);
return new DiffResult(jsonBefore, jsonAfter);
}
private static String highlightDifference(JsonNode node1, JsonNode node2, List<String> fieldsToMonitor) {
StringBuilder html = new StringBuilder();
if (node1.isObject() && node2.isObject()) {
ObjectNode objectNode1 = (ObjectNode) node1;
ObjectNode objectNode2 = (ObjectNode) node2;
for (String field : fieldsToMonitor) {
JsonNode fieldNode1 = findField(objectNode1, field);
JsonNode fieldNode2 = findField(objectNode2, field);
if (fieldNode1 != null && fieldNode2 != null) {
if (!fieldNode1.equals(fieldNode2)) {
html.append("<span style='background-color: yellow;'>").append(field).append(": ").append(fieldNode1.asText()).append("</span><br>");
}
} else if (fieldNode1 != null) {
html.append("<span style='background-color: yellow;'>").append(field).append(": ").append(fieldNode1.asText()).append("</span><br>");
} else if (fieldNode2 != null) {
html.append("<span style='background-color: yellow;'>").append(field).append(": ").append(fieldNode2.asText()).append("</span><br>");
}
}
}
return html.toString();
}
private static JsonNode findField(ObjectNode node, String fieldName) {
if (node.has(fieldName)) {
return node.get(fieldName);
}
for (Iterator<String> it = node.fieldNames(); it.hasNext(); ) {
String key = it.next();
JsonNode childNode = node.get(key);
if (childNode.isObject()) {
JsonNode result = findField((ObjectNode) childNode, fieldName);
if (result != null) {
return result;
}
}
}
return null;
}
public static DiffResult generateHtmlWithHighlights(JsonNode node1, JsonNode node2) {
try {
// 获取差异并进行高亮处理
Map<String, String> differences = getDifferences(node1, node2);
ObjectMapper newMapper = new ObjectMapper();
ObjectNode highlightedNode = newMapper.createObjectNode();
highlightDifference(node1, differences, highlightedNode, "");
// 将差异节点转换为字符串
String beforeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(highlightedNode);
highlightedNode = newMapper.createObjectNode();
highlightDifference(node2, differences, highlightedNode, "");
// 将差异节点转换为字符串
String afterStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(highlightedNode);
// 创建 DiffResult 对象并返回
return new DiffResult(beforeStr, afterStr);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取两个 JSON 对象的差异
* @param node1 before 节点
* @param node2 after 节点
* @return
*/
private static Map<String, String> getDifferences(JsonNode node1, JsonNode node2) {
Map<String, String> differences = new LinkedHashMap<>();
compareJsonNodes(node1, node2, differences);
return differences;
}
/**
* 递归比较两个 JSON 节点的差异
* @param node1 before 节点
* @param node2 after 节点
* @param differences 差异字段
*/
private static void compareJsonNodes(JsonNode node1, JsonNode node2, Map<String, String> differences) {
Iterator<String> fieldNames = node1.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode fieldValue1 = node1.get(fieldName);
JsonNode fieldValue2 = node2.get(fieldName);
// 如果是对象类型,则继续比较
if (fieldValue1.isObject() && fieldValue2.isObject()) {
compareJsonNodes(fieldValue1, fieldValue2, differences);
// 跳过本次后续操作
continue;
}
if (!Objects.equals(fieldValue1, fieldValue2)) {
differences.put(fieldName, fieldValue1.toString());
}
}
}
private static void highlightDifference(JsonNode node, Map<String, String> differences,
ObjectNode highlightedNode, String key) {
try {
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
ObjectNode objectNode = null;
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
String fieldName = field.getKey();
JsonNode fieldValue = field.getValue();
if (fieldValue.isObject()) {
// 如果是对象,递归查看内部属性;TODO 其实可以将内部value转map,查看是否存在differences中的字段。是则递归
log.info("key: {}, fieldName: {}", key, fieldName);
highlightDifference(fieldValue, differences, highlightedNode, fieldName);
}
if (StrUtil.isNotEmpty(key)) {
if (ObjUtil.isEmpty(objectNode)) {
// 初始化 objectNode
objectNode = new ObjectMapper().createObjectNode();
}
// 差异化包含 fieldName 设置html元素
if (differences.containsKey(fieldName)) {
objectNode.put(fieldName,
"<span style='background-color: yellow;'>" + fieldValue + "</font>");
} else {
// 差异化不包含正常添加数据
objectNode.set(fieldName, fieldValue);
}
} else {
if (differences.containsKey(fieldName)) {
highlightedNode.put(fieldName,
"<span style='background-color: yellow;'>" + fieldValue + "</font>");
} else {
// 避免原生对象数据后期替换掉之前设置的 objectNode
if (highlightedNode.get(fieldName) == null) {
highlightedNode.set(fieldName, fieldValue);
}
}
}
}
if (ObjUtil.isNotEmpty(objectNode) && StrUtil.isNotEmpty(key)) {
highlightedNode.set(key, objectNode);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Data
public static class DiffResult {
private String before;
private String after;
public DiffResult(String before, String after) {
this.before = before;
this.after = after;
}
}
}