一、概述
ModelDiffUtil
的工具类,用于比较两个对象之间的差异,并将差异以Map的形式返回。该类的主要方法是diffToMap
,它接受两个对象作为输入,并可选地接受一个忽略字段的列表。该方法首先将输入对象转换为JSON字符串,然后将JSON字符串转换为Map,最后比较两个Map的差异并返回结果。该类还包含一些辅助方法,如convertJsonToMap
和doCompare
,用于处理JSON到Map的转换和比较两个Map的差异。
二、方法说明
1、getDiffMap(T sourceObject, T targetObject)
将两个对象的差异转换为 Map
格式。
调用示例:
class User {
private String name;
private int age;
// 构造函数、getter 和 setter 方法
}
User sourceUser = new User();
sourceUser.setName("Alice");
sourceUser.setAge(25);
User targetUser = new User();
targetUser.setName("Bob");
targetUser.setAge(30);
Map<String, Map<String, String>> diffMap = ModelDiffUtil.getDiffMap(sourceUser, targetUser);
执行结果:
{
"name": {
"source": "Alice",
"target": "Bob"
},
"age": {
"source": "25",
"target": "30"
}
}
2、getDiffMap(T sourceObject, T targetObject, List<String> ignoreFields)
将两个对象的差异转换为 Map
格式,可以指定忽略的字段。
调用示例:
List<String> ignoreFields = Arrays.asList("age");
User sourceUser = new User();
sourceUser.setName("Alice");
sourceUser.setAge(25);
User targetUser = new User();
targetUser.setName("Bob");
targetUser.setAge(30);
Map<String, Map<String, String>> diffMap = ModelDiffUtil.getDiffMap(sourceUser, targetUser, ignoreFields);
执行结果:
{
"name": {
"source": "Alice",
"target": "Bob"
}
}
3、 getDiffMap(String sourceJsonStr, String targetJsonStr)
从两个 JSON 字符串中提取差异。
调用示例:
String sourceJsonStr = "{\"name\":\"Alice\",\"age\":25}";
String targetJsonStr = "{\"name\":\"Bob\",\"age\":30}";
Map<String, Map<String, String>> diffMap = ModelDiffUtil.getDiffMap(sourceJsonStr, targetJsonStr);
执行结果:
{
"name": {
"source": "Alice",
"target": "Bob"
},
"age": {
"source": "25",
"target": "30"
}
}
4、 diffToMap(String sourceJsonStr, String targetJsonStr, List<String> ignoreFields)
从两个 JSON 字符串中提取差异,可以指定忽略的字段。
调用示例:
String sourceJsonStr = "{\"name\":\"Alice\",\"age\":25}";
String targetJsonStr = "{\"name\":\"Bob\",\"age\":30}";
List<String> ignoreFields = Arrays.asList("age");
Map<String, Map<String, String>> diffMap = ModelDiffUtil.getDiffMap(sourceJsonStr, targetJsonStr, ignoreFields);
执行结果:
{
"name": {
"source": "Alice",
"target": "Bob"
}
}
三、内部方法
1、convertJsonToMap(Object json, String root, Map<String, Object> resultMap, List<String> ignoreFields)
- 功能:将
JSON
对象转换为Map
,忽略指定的字段。 - 这是一个内部辅助方法,通常不会被外部直接调用。
2、doCompare(Map<String, Object> sourceMap, Map<String, Object> targetMap)
- 功能:执行实际的比较操作,更新
sourceMap
以反映差异。 - 这是一个内部辅助方法,通常不会被外部直接调用。
compareSourceMapWithTargetMap(Map<String, Object> sourceMap)
- 功能:比较
sourceMap
和targetMap
,处理sourceMap
中独有的项。 - 这是一个内部辅助方法,通常不会被外部直接调用。
compareTargetMapWithSourceMap(Map<String, Object> sourceMap, Map<String, Object> targetMap)
-
功能:比较
sourceMap
和targetMap
,处理targetMap
中独有的或已更改的项。 - 这是一个内部辅助方法,通常不会被外部直接调用。 -
processEntry(Map<String, Object> sourceMap, String key, Object value, Object targetValue)
- 功能:处理单个条目的逻辑,根据提供的值构建差异映射或移除条目。 - 这是一个内部辅助方法,通常不会被外部直接调用。 这个工具类在需要比较对象差异并以特定格式获取结果的场景中非常有用,例如数据同步、版本控制、数据审计等。
二、完整的代码
pom依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.8</version>
</dependency>
完整代码:
import com.ecwid.consul.json.GsonFactory;
import com.google.gson.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* @author: fhey
* @create: 2022-04-26 15:29
* @description: 对象比较工具类
*/
@Slf4j
public class ModelDiffUtil {
private static final Gson gson = GsonFactory.getGson();
private final static String SOURCE_VALUE = "source";
private final static String TARGET_VALUE = "target";
private final static String DOT_SEPARATOR = ".";
/**
* 将两个对象的差异转换为Map格式。
*
* @param sourceObject 原始对象
* @param targetObject 当前对象
* @return 包含差异的Map
*/
public static<T> Map<String, Map<String, String>> getDiffMap(T sourceObject, T targetObject) {
return getDiffMap(sourceObject, targetObject, null);
}
/**
* 将两个对象的差异转换为Map格式,可以指定忽略的字段。
*
* @param sourceObject 原始对象
* @param targetObject 当前对象
* @param ignoreFields 忽略的字段列表
* @return 包含差异的Map
*/
public static <T> Map<String, Map<String, String>> getDiffMap(T sourceObject, T targetObject, List<String> ignoreFields) {
try {
String sourceJsonStr = gson.toJson(sourceObject);
String targetJsonStr = gson.toJson(targetObject);
return getDiffMap(sourceJsonStr, targetJsonStr, ignoreFields);
} catch (JsonSyntaxException e) {
log.error("Failed to parse object to JSON", e);
return new HashMap<>();
}
}
/**
* 从JSON字符串中提取差异。
*
* @param sourceJsonStr 原始JSON字符串
* @param targetJsonStr 当前JSON字符串
* @return 包含差异的Map
*/
public static Map<String, Map<String, String>> getDiffMap(String sourceJsonStr, String targetJsonStr) {
return getDiffMap(sourceJsonStr, targetJsonStr, null);
}
/**
* 从JSON字符串中提取差异,可以指定忽略的字段。
*
* @param sourceJsonStr 原始JSON字符串
* @param targetJsonStr 当前JSON字符串
* @param ignoreFields 忽略的字段列表
* @return 包含差异的Map
*/
public static Map<String, Map<String, String>> getDiffMap(String sourceJsonStr, String targetJsonStr, List<String> ignoreFields) {
try {
JsonObject sourceJson = new JsonParser().parse(sourceJsonStr).getAsJsonObject();
JsonObject targetJson = new JsonParser().parse(targetJsonStr).getAsJsonObject();
Map<String, String> sourceMap = new LinkedHashMap<>();
Map<String, String> targetMap = new LinkedHashMap<>();
convertJsonToMap(sourceJson, StringUtils.EMPTY, sourceMap, ignoreFields);
convertJsonToMap(targetJson, StringUtils.EMPTY, targetMap, ignoreFields);
return doCompare(sourceMap, targetMap);
} catch (JsonSyntaxException e) {
log.error("Failed to parse JSON string", e);
return new HashMap<>();
}
}
/**
* 将JSON对象转换为Map,忽略指定的字段。
*
* @param json JSON对象
* @param root 当前JSON路径
* @param resultMap 存储转换结果的Map
* @param ignoreFields 忽略的字段列表
*/
/**
* 将JSON对象转换为Map,忽略指定的字段。
*
* @param json JSON对象
* @param root 当前JSON路径
* @param resultMap 存储转换结果的Map
* @param ignoreFields 忽略的字段列表
*/
private static void convertJsonToMap(JsonElement json, String root, Map<String, String> resultMap, List<String> ignoreFields) {
if (json.isJsonObject()) {
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
String key = entry.getKey();
if (CollectionUtils.isNotEmpty(ignoreFields) && ignoreFields.contains(key)) {
continue;
}
JsonElement value = entry.getValue();
String newRoot = (root.isEmpty())? key : root + DOT_SEPARATOR + key;
if (value.isJsonObject() || value.isJsonArray()) {
convertJsonToMap(value, newRoot, resultMap, ignoreFields);
} else if(value.isJsonPrimitive()){
resultMap.put(newRoot, value.getAsJsonPrimitive().getAsString());
}
}
} else if (json.isJsonArray()) {
JsonArray jsonArray = json.getAsJsonArray();
for (int i = 0; i < jsonArray.size(); i++) {
JsonElement value = jsonArray.get(i);
String newRoot = (root.isEmpty())? "[" + i + "]" : root + "[" + i + "]";
if (value.isJsonObject() || value.isJsonArray()) {
convertJsonToMap(value, newRoot, resultMap, ignoreFields);
} else if(value.isJsonPrimitive()){
resultMap.put(newRoot, value.getAsJsonPrimitive().getAsString());
}
}
}
}
/**
* 执行实际的比较操作,返回包含差异的Map
*
* @param sourceMap 原始Map
* @param targetMap 当前Map
* @return 包含差异的Map
*/
private static Map<String, Map<String, String>> doCompare(Map<String, String> sourceMap, Map<String, String> targetMap) {
Map<String, Map<String, String>> diffMap = new HashMap<>();
for (Map.Entry<String, String> entry : targetMap.entrySet()) {
String key = entry.getKey();
String newValue = entry.getValue();
String oldValue = sourceMap.get(key);
if (sourceMap.containsKey(key)) {
if (!ObjectUtils.equals(newValue, oldValue)) {
addDiffMap(diffMap, key, oldValue, newValue);
}
} else {
addDiffMap(diffMap, key, StringUtils.EMPTY, newValue);
}
}
return diffMap;
}
/**
* 将差异项添加到差异映射中。
*
* 此方法用于在处理两个数据集的差异时,将特定的差异项添加到一个映射中,
* 其中每个差异项由一个键值对表示,包括源值和目标值。
*
* @param diffMap 保存差异项的映射,其中键是差异项的标识,值是包含源值和目标值的映射。
* @param key 差异项的标识,用于在diffMap中作为键。
* @param value 源对象中的值,表示差异的起始点。
* @param targetValue 目标对象中的值,表示与源值不同的目标值。
*/
private static void addDiffMap(Map<String, Map<String, String>> diffMap, String key, String value, String targetValue) {
Map<String, String> diffItemMap = new HashMap<>();
diffItemMap.put(SOURCE_VALUE, value);
diffItemMap.put(TARGET_VALUE, targetValue);
diffMap.put(key, diffItemMap);
}
}