对象比较工具类:实现对业务的修改记录保存(对象字段差异对比)

news2024/11/23 4:31:54

测试

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);
    }
}
测试:
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);

    }
}
结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2203017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宠物空气净化器哪个牌子吸毛好?希喂、IAM、352真实测评

前段时间忙活了个大工程——热门宠物空气净化器大横评&#xff0c;通过多方位的测试&#xff0c;帮助不少朋友解决了宠物空气净化器的选购难题。 但我们还不局限于此&#xff0c;在从事家电行业这些年间&#xff0c;我们还花费大价钱购置一堆产品实测体验过各类大小家电产品&a…

Spring Boot教学资源库:从入门到精通

1绪 论 1.1研究背景 目前&#xff0c;在网络大环境下&#xff0c;越来越多高校开始实行网络教学&#xff0c;利用网络教学方式有利于学生更好的学习。 网络教学是指以计算机及网络为基础&#xff0c;来实现教学资源的上传、存储、传播和共享的教学手段。它是一种教学活动&#…

PyQt5 基于paho-mqtt库 实现MQTT通信

PyQt5 基于paho-mqtt库 实现MQTT通信 paho-mqtt安装paho-mqtt库综合示例错误处理 paho-mqtt paho-mqtt官网文档 安装paho-mqtt库 pip install paho-mqtt 综合示例 封装MQTT类订阅消息发布消息信号方式接收处理MQTT消息 import paho.mqtt.client as mqtt import sys import …

在线绘图工具drawio,visio的平替

Draw.io&#xff1a;灵活高效的在线绘图工具推荐 在工作和项目管理中&#xff0c;流程图、架构图和思维导图等可视化图表是非常重要的沟通工具。Draw.io&#xff08;现更名为diagrams.net&#xff09;是一个强大且免费的在线绘图工具&#xff0c;适用于创建各种类型的图表。它功…

YOLOv11训练自己的数据集,YOLOv11网络解析

1 训练自己的数据集 在github搜索ultralytics并下载。 GitHub - ultralytics/ultralytics: Ultralytics YOLO11 &#x1f680; 环境配置不再赘述&#xff0c;本地配置自行搜索教程&#xff0c;若使用云服务器配置更为简单。 数据标注 pip install labelimg启动标注工具 la…

【PGCCC】在 Postgres 上构建图像搜索引擎

我最近看到的最有趣的电子商务功能之一是能够搜索与我手机上的图片相似的产品。例如&#xff0c;我可以拍一双鞋或其他产品的照片&#xff0c;然后搜索产品目录以查找类似商品。使用这样的功能可以是一个相当简单的项目&#xff0c;只要有合适的工具。如果我们可以将问题定义为…

Pycharm设置相同变量的背景颜色

在 PyCharm 中设置鼠标点击某个变量时&#xff0c;代码其他地方相同变量的背景颜色&#xff0c;可以通过调整颜色方案中的相关设置来实现。以下是详细步骤&#xff1a; 打开 PyCharm&#xff0c;选择 File -> Settings&#xff08;在 Windows/Linux 上&#xff09;或 PyChar…

基于 CSS Grid 的简易拖拉拽 Vue3 组件,从代码到NPM发布(1)- 拖拉拽交互

基于特定的应用场景&#xff0c;需要在页面中以网格的方式&#xff0c;实现目标组件在网格中可以进行拖拉拽、修改大小等交互。本章开始分享如何一步步从代码设计&#xff0c;最后到如何在 NPM 上发布。 请大家动动小手&#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug…

全网最详细k8s搭建部署

目录 Kubernetes的功能&#xff1a; Kubernetes的特点&#xff1a; 1. 安装要求 2. 部署内容 1、系统环境准备 2、所有禁用swap和本地解析 3、仓库配置&#xff0c;所有安装docker 4、所有节点设定docker的资源管理模式为systemd 5、所有阶段复制harbor仓库中的证书并…

一款电子产品图册转换器

​随着科技的不断发展&#xff0c;电子产品已经成为我们生活中不可或缺的一部分。无论是手机、平板电脑还是智能家居&#xff0c;它们都离不开电子图册的支撑。一款优秀的电子产品图册转换器&#xff0c;可以帮助我们轻松实现电子图册的转换&#xff0c;为我们的生活和工作带来…

AlphaFold加冕诺奖,DeepMind CEO获奖感言:最优秀的科学家与AI配合,将完成令人难以置信的工作

继「AI 教父」摘冠 2024 年诺贝尔物理学奖后&#xff0c;AI 再下一城&#xff0c;获得了今年的诺贝尔化学奖。 北京时间 10 月 9 日&#xff0c;瑞典皇家科学院宣布了 2024 年诺贝尔化学奖的归属&#xff0c;一半授予 David Baker&#xff0c; 以表彰其在计算蛋白设计方面的贡…

python实现音频文件mp3/m4a转.wav + windows安装ffmpeg

近期在尝试使用大模型进行音频降噪、人声分离。抱脸上的模型几乎统一输入需求都是.wav&#xff0c;直接贴代码吧 ps:使用这段代码需要提前安装好ffmpeg(安装教程请往下拉) from pydub import AudioSegment# 加载.m4a文件 audio AudioSegment.from_file(r"你文件的路径&…

Pycharm使用CV2

1、windows下已经安装好python3以及opencv2 2、安装并打开pycharm 环境中装好的包就能显示出来了&#xff0c;就可以去调用cv2的接口了

TCP/IP相关

1、关于三次握手、四次挥手和TCP的11种状态&#xff1a; 记住这张图就行了&#xff1a; 2、关于慢启动、拥塞避免、超时重传、快速重传、快速恢复 记住这张图就行了&#xff1a; 一些名词解释&#xff1a; MSS&#xff1a;Maximum Segment Size&#xff0c;最大报文长度 RT…

力扣 1206. 设计跳表

Problem: 1206. 设计跳表 &#x1f469;‍&#x1f3eb; 参考题解 class Skiplist {// 定义跳表的最大层数int level 10;// 定义跳表节点类class Node {int val; // 节点值Node[] ne new Node[level]; // 节点的下一跳节点数组&#xff0c;支持多级索引// 构造函数&#xf…

深度学习之卷积CONV2D

文章目录 1.学习目的2.填充与步幅2.1填充2.2 步幅 3.总结 1.学习目的 卷积听起来简单&#xff0c;事实上不简单&#xff0c;需要多加练习 2.填充与步幅 在前面的例子 图6.2.1中&#xff0c;输入的高度和宽度都为3&#xff0c;卷积核的高度和宽度都为2&#xff0c;生成的输出…

用Python将HTML转换为Excel文件

在数据处理和分析的过程中&#xff0c;经常需要从网页上抓取信息&#xff0c;并将其转换为更易于操作的格式。HTML表格作为一种常见的数据展示方式&#xff0c;在线报告、统计资料等场景中广泛存在&#xff0c;但其结构化程度较低&#xff0c;不利于进一步的数据清洗和分析。将…

微信小程序-APP-软件开发

微信小程序开发&#xff0c;作为当下移动互联网领域的一股强劲势力&#xff0c;正以其便捷性、轻量化及高用户粘性的特点&#xff0c;深刻改变着我们的生活与工作方式。它不仅为企业和个人开发者提供了一个全新的服务入口&#xff0c;更极大地拓宽了商业应用的边界。 在微信小…

2015年国赛高教杯数学建模D题众筹筑屋规划方案设计解题全过程文档及程序

2015年国赛高教杯数学建模 D题 众筹筑屋规划方案设计 众筹筑屋是互联网时代一种新型的房地产形式。现有占地面积为102077.6平方米的众筹筑屋项目&#xff08;详情见附件1&#xff09;。项目推出后&#xff0c;有上万户购房者登记参筹。项目规定参筹者每户只能认购一套住房。  …

游戏录屏必备!五款超实用软件让你轻松记录精彩游戏瞬间

在游戏的世界里&#xff0c;每一个精彩的操作、每一场激烈的对战都值得被记录下来。无论是想要分享给朋友&#xff0c;还是留作自己的游戏回忆&#xff0c;一款优秀的游戏录屏软件都是必不可少的。下面就为大家介绍五款备受好评的游戏录屏软件&#xff0c;让你轻松成为游戏录屏…