测试
1:User类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@FieldLabel("姓名")
private String name;
@FieldLabel("年龄")
private Integer age;
@FieldLabel("手机")
private String phone;
@FieldLabel("手机号")
private String phoneNumber;
@Nullable
private Address address;
}
2:自定义注解
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface FieldLabel {
String value(); // 中文标识
}
3:工具类
依赖:
<!-- google java lib -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
utils:
package com.example.juc.utils.比较对象的差异工具类;
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 org.springframework.http.converter.json.GsonFactoryBean;
import java.util.*;
/**
* @author: xlj
* @create: 2024-10-09 15:29
* @description: 对象比较工具类
*/
@Slf4j
public class ModelDiffUtil {
private static final Gson gson = new Gson();
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);
}
}
比较2个不同类型的字段是否有差异
package tech.tongyu.etrade.trsmanager.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;
import java.math.BigDecimal;
import java.util.*;
public class ObjectDiffUtil {
private static final ObjectMapper mapper = new ObjectMapper();
/**
* 比较两个对象是否有差异
*/
public static boolean isChanged(Object oldObj, Object newObj) {
JsonNode oldNode = oldObj == null ? NullNode.getInstance() : mapper.valueToTree(oldObj);
JsonNode newNode = newObj == null ? NullNode.getInstance() : mapper.valueToTree(newObj);
return isChangedJsonNode(oldNode, newNode);
}
private static boolean isChangedJsonNode(JsonNode oldNode, JsonNode newNode) {
// 都为 null
if (oldNode == null || oldNode.isNull()) {
return !(newNode == null || newNode.isNull());
}
if (newNode == null || newNode.isNull()) {
return true;
}
// 数组/集合(顺序不敏感)比较
if (oldNode.isArray() && newNode.isArray()) {
if (oldNode.size() != newNode.size()) return true;
List<JsonNode> oldList = new ArrayList<>();
oldNode.forEach(oldList::add);
List<JsonNode> newList = new ArrayList<>();
newNode.forEach(newList::add);
// 用计数 map 判断集合是否相等
Map<String, Integer> countMap = new HashMap<>();
for (JsonNode node : oldList) {
String key = node.toString();
countMap.put(key, countMap.getOrDefault(key, 0) + 1);
}
for (JsonNode node : newList) {
String key = node.toString();
if (!countMap.containsKey(key)) return true;
countMap.put(key, countMap.get(key) - 1);
if (countMap.get(key) == 0) countMap.remove(key);
}
return !countMap.isEmpty();
}
// 对象比较
if (oldNode.isObject() && newNode.isObject()) {
Iterator<String> fieldNames = oldNode.fieldNames();
while (fieldNames.hasNext()) {
String field = fieldNames.next();
JsonNode oldField = oldNode.get(field);
JsonNode newField = newNode.get(field);
if (isChangedJsonNode(oldField, newField)) {
return true;
}
}
// 检查 newNode 是否有 oldNode 没有的字段
Iterator<String> newFieldNames = newNode.fieldNames();
while (newFieldNames.hasNext()) {
String field = newFieldNames.next();
if (!oldNode.has(field)) return true;
}
return false;
}
// 数值类型统一用 BigDecimal 比较
if (oldNode.isNumber() && newNode.isNumber()) {
BigDecimal oldNum = oldNode.decimalValue();
BigDecimal newNum = newNode.decimalValue();
return oldNum.compareTo(newNum) != 0;
}
// 时间类型或字符串类型直接比较
if (oldNode.isTextual() && newNode.isTextual()) {
return !oldNode.asText().equals(newNode.asText());
}
// 布尔类型
if (oldNode.isBoolean() && newNode.isBoolean()) {
return oldNode.asBoolean() != newNode.asBoolean();
}
// 默认 JsonNode equals
return !oldNode.equals(newNode);
}
}
测试:
public class 对象字段差异 {
public static void main(String[] args) {
User user = new User();
user.setName("夏天");
user.setAge(18);
user.setPhone("苹果");
user.setPhoneNumber("177");
List<String> ignoreFields = Collections.singletonList("address");
User user1 = new User();
user1.setName("夏天");
user1.setAge(188);
user1.setPhone("华为");
user1.setPhoneNumber("177");
Map<String, Map<String, String>> diffMap = ModelDiffUtil.getDiffMap(user, user1, ignoreFields);
System.out.println(diffMap);
Map<String, String> map = AnnotationUtil.printFieldLabels(User.class);
System.out.println(map);
System.out.println("--------------------------");
List<String> diff = new java.util.ArrayList<>(Collections.emptyList());
diffMap.forEach((k, v) -> {
String object = map.get(k);
if (StrUtil.isNotBlank(object)) {
diff.add(object);
}
});
System.out.println(diff);
}
}
结果:

&spm=1001.2101.3001.5002&articleId=142782089&d=1&t=3&u=6498c4e4ecc94a8f9a722d066c40e124)

被折叠的 条评论
为什么被折叠?



