对比两个json串的diff,支持map的深度递归
背景
项目重构,对老接口进行技术改造。动代码后,难免会有些bug,我们需要对比改造前后接口的返回,来判断逻辑是否有问题,这就涉及两个json的对比。
常规的diff文本工具是按行对比,无法处理复杂的map。本文通过gson来解析json, 进而递归寻找diff。
依赖
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version>
</dependency>
代码
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.tencent.trpc.core.utils.JsonUtils;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;public class Test {public static Set<String> diffKeySet = new HashSet<>();// 自定义需要忽略的字段public static Set<String> ignores = Sets.newHashSet("code","message");public static void main(String[] args) throws Exception {String formalStr = loadText("../formalStr.txt");String devStr = loadText("../devStr.txt");JsonObject formal = formal(formalStr);JsonObject dev = dev(devStr);findDiff("", formal, dev);filter();System.out.println(JsonUtils.toJson(diffKeySet));System.out.println(JsonUtils.toJson(diffKeySet.stream().map(s -> s.replaceAll("[\\d]", "")).collect(Collectors.toSet())));}private static void filter() {diffKeySet.removeIf(key -> ignores.stream().anyMatch(key::contains));}private static String loadText(String filePath) throws Exception {FileInputStream inputStream = null;BufferedReader bufferedReader = null;StringBuilder builder = new StringBuilder();try {String line;inputStream = new FileInputStream(filePath);bufferedReader = new BufferedReader(new InputStreamReader(inputStream));while ((line = bufferedReader.readLine()) != null) {builder.append(line.trim());}return builder.toString();} finally {close(inputStream, bufferedReader);}}public static void close(FileInputStream inputStream, BufferedReader bufferedReader) {try {if (inputStream != null) {inputStream.close();}if (bufferedReader != null) {bufferedReader.close();}} catch (IOException e) {e.printStackTrace();}}private static void findDiff(String prefix, JsonObject formal, JsonObject dev) {Set<String> formalKeySet = formal.keySet();formalKeySet.forEach(key -> {JsonElement formalElement = formal.get(key);JsonElement devElement = dev.get(key);findElementDiff(prefix, key, formalElement, devElement);});}public static void findElementDiff(String prefix, String key, JsonElement formalElement, JsonElement devElement) {String diffKeyPath = prefix + ":" + key;if (isNull(formalElement, devElement, diffKeyPath)) {return;}if (formalElement.isJsonObject()) {// 处理mapcheckObject(formalElement, devElement, diffKeyPath);return;}if (formalElement.isJsonArray()) {// 处理集合checkCollection(prefix, key, formalElement, devElement, diffKeyPath);return;}//if (formalElement.isJsonPrimitive()) {// 基本类型if (!formalElement.getAsString().equals(devElement.getAsString())) {addDiff(diffKeyPath);}//}}private static boolean isNull(JsonElement formalElement, JsonElement devElement, String diffKeyPath) {if (Objects.isNull(formalElement) || formalElement.isJsonNull()) {if (Objects.isNull(devElement) || devElement.isJsonNull()) {return true;}addDiff(diffKeyPath);return true;}if (Objects.isNull(devElement) || devElement.isJsonNull()) {addDiff(diffKeyPath);return true;}return false;}private static void checkObject(JsonElement formalElement, JsonElement devElement, String diffKeyPath) {if (!devElement.isJsonObject()) {addDiff(diffKeyPath);return;}JsonObject formalObject = formalElement.getAsJsonObject();JsonObject devObject = devElement.getAsJsonObject();findDiff(diffKeyPath, formalObject, devObject);}private static void checkCollection(String prefix, String key, JsonElement formalElement, JsonElement devElement,String diffKeyPath) {if (!devElement.isJsonArray()) {addDiff(diffKeyPath);return;}JsonArray formalArr = formalElement.getAsJsonArray();JsonArray devArr = devElement.getAsJsonArray();int size = formalArr.size();if (size != devArr.size()) {addDiff(diffKeyPath);return;}for (int i = 0; i < size; i++) {JsonElement formalArrElement = formalArr.get(i);JsonElement devArrElement = devArr.get(i);findElementDiff(prefix, String.format("%s[%d]", key, i), formalArrElement, devArrElement);}}public static void addDiff(String path) {diffKeySet.add(path);}public static JsonObject dev(String devJson) {return new Gson().fromJson(devJson, JsonObject.class);}public static JsonObject formal(String formalJson) {return new Gson().fromJson(formalJson, JsonObject.class);}
使用方式
将不同环境的json串分别放到txt中,调整java脚本中的文件地址,运行即可。大家结合自身诉求,按需调整脚本。
以文本1作为标准,脚本返回两行数据:
文本1中与文本2不同的所有diff,list对象不计入下标;
文本1中与文本2不同的去重后的diff,list对象不计入下标;
相关文章:
对比两个json串的diff,支持map的深度递归
背景 项目重构,对老接口进行技术改造。动代码后,难免会有些bug,我们需要对比改造前后接口的返回,来判断逻辑是否有问题,这就涉及两个json的对比。 常规的diff文本工具是按行对比,无法处理复杂的map。本文通…...
【我的创作纪念日1024】
我的创作纪念日1024 机缘成就明年的规划 机缘 过去的1024个日子里,我在专业发展、职场和发展、科技创新创业、软件开发、人工智能、虚拟现实、区块链等栏目分享了一些工作和学习的建议和体会。尤其是在2022年,我连续发布100篇的博文,不仅仅是…...
萤石设备视频接入平台EasyCVR私有化视频平台变电站如何实现远程集中监控?
一、方案背景 随着城市经济的发展和电力系统的改造,变电站的数量和规模逐渐增加,对变电站的安全管理和监控需求也越来越高。视频监控系统作为重要的安全管理手段,在变电站中起到了关键的作用。 目前青犀视频研发的萤石设备视频接入平台EasyC…...
什么是多线程?请描述 Java 中实现多线程的基本方式?
今天和大家探讨一下 Java 中的多线程,包括它的基本概念、实现方式以及一些实际开发中的注意事项。 什么是多线程? 多线程是指在一个程序中存在多个执行流,每个执行流都可以独立于其他执行流执行。 在 Java 中,多线程允许开发者…...
Dynamic Sparse No Training: Training-Free Fine-tuning for Sparse LLMs
大语言模型(LLM)在设备上部署道路上落下了一个令人生畏的障碍。本文关注于大语言模型的剪枝算法。 动态稀疏训练(Dynamic Sparse Training,DST)是一种近期收到广泛关注的剪枝算法。与之前大部分剪枝方法需要训练整个网…...
解决n+1查询数据库问题
文章目录 1. 问题描述2. 解决方法3. 总结 1. 问题描述 在写项目中,可能会碰到一个问题:通过查询表A得到一个list结果,再对list中的n个元素各查询一次关联的表B。形成对数据库执行n1次查询。这种代码会无形增加数据库的处理负担,影…...
DICOM 基础知识:深入理解DICOM数据结构与标签说明
目录 DICOM 图像概念 DICOM 图像关键特性: DICOM 文件结构 常见数据元素: 数据元素示例详解 DICOM-VR 数据类型说明 DICOM 标准支持的数据集 结语 DICOM 图像概念 DICOM(Digital Imaging and Communications in Medicine&…...
Git - 如何删除 push 过一次的文件链路追踪?
(以 target 文件夹为例)如果你已经在 .gitignore 中添加了 target/ 目录,但 target 文件夹仍然出现在 Git 的变更列表中,可能是因为它之前已经被添加到 Git 仓库中。即使你更新了 .gitignore,Git 仍然会跟踪这些文件。…...
软件测试学习总结
一.软件测试概念和目的 软件测试的概念: 测试模型(V模型) 软件测试就是在软件投入运行前,对软件需求分析、设计规格说明和编码实现的最终审查,它是软件质量保证的关键步骤。 通常对软件测试的定义有两种描述: 定义1:软件测试是为了发现错误而执行程序的过程 定义2:…...
c语言错题——#define对应的查找替换
文章目录 一、题目 提示:以下是本篇文章正文内容,下面案例可供参考 一、题目 分析 结构体向最长的char对齐,前两个位段元素一共42位,不足8位,合起来占1字节,最后一个单独1字节,一共3字节。另外…...
Visual Basic介绍及简单例子
Visual Basic(简称 VB)是一种由微软公司开发的包含协助开发环境的事件驱动编程语言。 一、主要特点 易于学习和使用: Visual Basic 具有直观的可视化开发环境,使用户可以通过拖放控件和设置属性的方式快速创建用户界面。对于初学者来说,这种方式非常容易上手,无需深入了…...
Matlab学习01-矩阵
目录 一,矩阵的创建 1,直接输入法创建矩阵 2,利用M文件创建矩阵 3,利用其它文本编辑器创建矩阵 二,矩阵的拼接 1,基本拼接 1) 水平方向的拼接 2)垂直方向的拼接 3…...
【复旦微FM33 MCU 外设开发指南】外设篇1——硬件除法器
前言 本系列基于复旦微FM33LC0系列单片机的DataSheet编写,旨在提供一些开发指南。 本文章及本系列其他文章将持续更新,本系列其它文章请跳转【复旦微FM33 MCU 外设开发指南】总集篇 本文章最后更新日期:2024/10/24 文章目录 前言用途工作流…...
在元神操作系统启动时自动执行任务脚本
1. 背景 本文主要介绍让元神操作系统启动时自动执行任务脚本的方法,适用于无人化任务执行目的。将任务脚本及相关的应用程序准备好之后,把装有元神操作系统的U盘插入目标电脑,然后打开电脑电源就会自动完成所设置的任务。 2. 方法 &#x…...
JAVA学习-练习试用Java实现“判断是否为等边三角形的方法”
问题: 定义一个三角形类(Triangle),包含三个边长(a, b, c)属性,并实现一个判断是否为等边三角形的方法。 解答思路: 下面是一个简单的 Triangle 类定义,其中包含了三个…...
Leetcode 140 Word Break II
题意:给定一个string以及一个wordDict,要求返回一个vector<string> ,这个vector中的string都是word Dict中的组合 Input: s “catsanddog”, wordDict [“cat”,“cats”,“and”,“sand”,“dog”] Output: [“cats and dog”,“cat sand dog”…...
文理学院数据库应用技术实验报告0
文理学院数据库应用技术实验报告0 实验内容 打开cmd,利用MySQL命令连接MySQL服务器。 mysql -u root -p查看当前MySQL服务实例使用的字符集(character)。 SHOW VARIABLES LIKE character_set_server;查看当前MySQL服务实例支持的字符序(collation)。 SHOW VARIABLES LIKE c…...
Bootstrap 4 按钮
Bootstrap 4 按钮 Bootstrap 4 是一个流行的前端框架,它提供了大量的组件和样式,用于快速开发响应式和移动设备优先的网页。在本文中,我们将重点讨论 Bootstrap 4 中的按钮组件,包括它们的基本用法、样式选项和自定义方法。 基本按钮 在 Bootstrap 4 中,创建一个基本按…...
【笔记】LLM位置编码之标准位置编码
标准位置编码 起源原理证明:对于任何固定的偏移量 k k k, P E p o s k PE_{posk} PEposk可以表示为 P E p o s PE_{pos} PEpos的线性函数。计算 P E p o s k 与 P E p o s PE_{posk} 与PE_{pos} PEposk与PEpos的内积结论 通俗理解缺点 起源 由…...
环 境 配 置
01 Ubuntu18.04中QT环境 1. 下载安装包 官网 http://download.qt.io/archive/qt/5.9/5.9.1/qt-opensource-linux-x64-5.9.1.run 国内镜像服务器 https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.9/5.9.1/qt-opensource-linux-x64-5.9.1.run QQ群 ...... 2. 安装 把下载…...
AI信息摘要系统:从RSS抓取到LLM摘要的自动化实现
1. 项目概述:AI驱动的每日信息摘要最近在GitHub上看到一个挺有意思的项目,叫“ai-daily-digest”。光看名字,你大概能猜到它想做什么:用人工智能来帮你整理每日信息。但具体怎么实现,能解决什么问题,背后又…...
单片机开发者如何通过Taotoken调用大模型API优化代码注释
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 单片机开发者如何通过Taotoken调用大模型API优化代码注释 对于单片机开发者而言,编写清晰、准确的代码注释是提升项目可…...
从FinFET到3D-IC:2013年预测如何塑造了今天的低功耗与异构计算设计
1. 项目概述:站在2013年初的十字路口十多年前,2013年初的那个冬天,整个半导体与电子设计自动化行业弥漫着一种既兴奋又焦虑的复杂情绪。当时,我作为行业里的一名技术编辑,向数十位来自芯片设计公司、EDA工具供应商、IP…...
2026年全国优质化妆培训机构深度盘点
颜值经济持续升温背景下,2026年化妆行业迎来规范化、专业化发展新阶段,涵盖影视、时尚、婚庆等多个领域,市场对专业化妆人才的需求持续攀升。据相关行业数据显示,2026年美业职业培训市场预计保持11%以上增速,美业门店专…...
Visual C++ 运行库终极修复指南:一键解决系统兼容性问题
Visual C 运行库终极修复指南:一键解决系统兼容性问题 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist VisualCppRedist AIO 是解决 Windows 系统 Vis…...
图像理解的底层逻辑:从像素到语义的三层跃迁
1. 这不是“看图说话”,而是让机器学会“看见”的底层逻辑 你有没有想过,当手机相册自动给你把“猫”和“狗”的照片分到不同相册里,或者修图App能一键抠出人像边缘、连发丝都清晰分明,背后到底发生了什么?很多人以为A…...
AI编码助手技能开发:基于Agent Skills打造智能命令行速查工具
1. 项目概述:一个能“听懂人话”的开发者命令行技能如果你和我一样,每天在终端和代码编辑器里花费大量时间,那你肯定对“命令遗忘症”深有体会。明明上周才用过git worktree来并行处理两个功能分支,今天突然想不起来具体的参数顺序…...
免费公式识别神器:img2latex-mathpix本地部署完全指南
免费公式识别神器:img2latex-mathpix本地部署完全指南 【免费下载链接】img2latex-mathpix Mathpix has changed their billing policy and no longer has free monthly API requests. This repo is now archived and will not receive any updates for the foresee…...
三步实现iOS虚拟定位:无需越狱的终极免费方案
三步实现iOS虚拟定位:无需越狱的终极免费方案 【免费下载链接】iFakeLocation Simulate locations on iOS devices on Windows, Mac and Ubuntu. 项目地址: https://gitcode.com/gh_mirrors/if/iFakeLocation iFakeLocation是一个专业级的iOS虚拟定位工具&am…...
无需写代码!用 PackSoft 做数字展厅大屏
前言 做过展厅项目的朋友都懂这种痛—— 客户参观来了,讲解员打开浏览器,地址栏、书签栏、收藏夹全暴露在屏幕上,旁边还挂着一个没关的 QQ 弹窗……高端大气的数字展厅,体验瞬间拉低一个档次。 更麻烦的是:大屏全屏…...
