一、摘要

今日推荐的是一款java中,比照两个json-diff目标是否共同的东西包 json-diff` 。他能够比照任何结构的两个json数据,而且将其中的不共同信息反馈给用户。东西还内置了许多装备能够来操控比照过程中的行为。现在现已补充大量单测,稳定性仍是比较好的。

二、布景

公司最近在重构一个中心体系,至于为什么重构原因许多,就不说明了。可是这个中心体系承载较多的线上事务。为了不影响依靠依靠该服务的应用,所以咱们重构的最中心就是彻底兼容老体系接口。

为了确保平滑上线,而且测验新体系与老体系是否共同,咱们决定体系并行一段时刻,而且在这段时刻之中验证新接口对老接口的兼容性。咱们新起一个代理服务,他会将咱们的用户流量分别转发到新老接口,然后拿到两个成果,将老接口成果直接回来;异步去比较新老成果是否契合预期,进行记录或者报警。

这样体系在经过一段时刻的测验,稳定性更高,犯错的概率更小。

json-diff简单使用

因为体系都是选用http接口对外供给服务,且回来数据格局一致的是json格局。所以咱们急需一款强大的Java言语的Json比照东西来协助咱们发现新老体系的不兼容之处。

三、东西介绍

1. 介绍

json-diff 是一款功用强大的json差异发现东西,支撑任何结构的json比照,而且能够将比照成果返给用户。现在该东西更新到了 3.0.0-RC1-RELEASE 版别。最新版能够查看 版别列表 。主张运用最新版,旧版或许存在缺点。

长处:

  • 轻量级:东西只依靠 fastjson2
  • 精准定位:能够回来最精准且详细的信息
  • 功用全面:简直掩盖任何json结构
  • 高功能

2. 运用教程

2.1 快速开始

  • 引入依靠
<dependency>
    <groupId>cn.xiaoandcai</groupId>
    <artifactId>json-diff</artifactId>
    <!-- 旧版别或许存在某些缺点。版别请以maven库房最版为准。 -->
    <version>${version}</version>
</dependency>

版别查看 2022-03-04 最新版别:3.0.0-RC1-RELEASE

  • 开始运用
/**
 * @author: codeleep
 * @createTime: 2022/11/22 16:57
 * @description: 运用示例
 */
public class UseExample {
    public static void main(String[] args) {
        String array1 = "[1, 2, 3, 4, 5]";
        String array2 = "[1, 3, 9, 4, 5]";
        JsonComparedOption jsonComparedOption = new JsonComparedOption().setIgnoreOrder(true);
        JsonCompareResult jsonCompareResult = new DefaultJsonDifference()
                .option(jsonComparedOption)
                .detectDiff(JSON.parseArray(array1), JSON.parseArray(array2));
        System.out.println(JSON.toJSONString(jsonCompareResult));
    }
}

成果展示:

{
    "defectsList": [
        {
            "actual": 9,
            "expect": 2,
            "illustrate": "The expect('2') data is inconsistent with the actual('9') data",
            "travelPath": {
                "abstractTravelPath": "root[]",
                "actualTravelPath": "root[2]",
                "expectTravelPath": "root[1]"
            }
        }
    ],
    "match": false
}

东西会回来 match 表明是否经过比对。defectsList 则是比照信息。

2.2 更多装备

装备 类型 备注
ignoreOrder boolean 是否比较过程中疏忽数组次序
mapping Map<String, String> 将实在字段映射到希望字段,key是实在字段name,value是希望的字段name
ignorePath Set<String> 当比照的途径彻底匹配时会被越过。遇到数组运用 [] 即可。无需填入下标
ignoreKey Set<String> 比照object时。或疏忽该key。对整个json收效
customComparator Map<String, Class<JsonNeat>> 用户自定义比较器。详细说明见下文

2.0.1-RC1-RELEASE 之后版别中移除了 keyFunction 装备参数。能够运用 ignorePath 来替代到达相同的效果。

东西供给了四个装备,来之比照过程中一些其他的要求。东西还在积极开发中,假如有新的需求,能够给作者提一个issuse。

在开发中。许多时候比照装备共同。能够运用 JsonDiffOption 进行敞开仅有装备

3. 进阶

3.1. 大局运用固定装备

因为在设计中考虑到各线程比较装备相互独立。所以默认将装备避免在 ThreadLocal 中进行存储。但在大多数情况下,咱们在大局比较时,装备并不会产生变化。

东西供给了大局装备办法。选用的办法是静态类特点。这样也会取得更好的功能。

// 敞开并设置大局装备
JsonDiffOption.openUniqueOption();
JsonDiffOption.setGloballyUniqueOption(new JsonComparedOption());
// 不想运用时能够调用调整回线程独有模式
 JsonDiffOption.closeUniqueOption();

3.2. 数组元素为目标关联

当咱们在遇到数组元素是一个目标时。如下:

[    {        "date": "23日星期五",        "sunrise": "06:16",        "high": "高温 18.0℃"    },    {        "date": "24日星期六",        "sunrise": "06:14",        "high": "高温 21.0℃"    }]

在比较时, 假如希望 date 字段共同,则以为两个目标共同。那么能够将 sunrise, high 字段都装备到 ignorePath 中。如:

HashSet<String> ignorePath = new HashSet<>();
ignorePath.add("root[].sunrise");
ignorePath.add("root[].high");

假如只是不想关注某个字段。便是 ignorePath 正常用法。装备如上。

3.3. 字段映射

在比较两个目标时。也许因为字段名变更。导致校验不经过。这时能够运用 mapping 装备。将 实在字段称号映射至希望字段称号。在比较过程中会将

actual.mappingKey 与 expect.mappingValue 以为是应该比较的目标。详细装备如下

// mapping key 是 actual 键名
// mapping value 是 expect 键名
HashMap<String, String> mapping = new HashMap<>();
mapping.put("date", "sunrise");

3.4. 字段疏忽

假如有一些字段是想在整个json都进行疏忽的,能够运用 ignoreKey 进行大局疏忽。当然假如不想大局疏忽,可是装备了该项,仍是会被疏忽掉。

HashSet<String> ignoreKey = new HashSet<>();
ignoreKey.add("sunrise");
ignoreKey.add("high");

3.5 自定义比较器

在咱们一个大json文件下。或许遇到某些节点希望完成自定义比较。能够经过 customComparator 来进行完成。

它装备的key是一个 travelPath 。详细格局参照 ignorePath 。value 则是一个自定义比较器。关于自定义比较器需求承继对应的笼统类。而且完成详细的笼统接口。详细如下:

目标比较:

需求承继 me.codeleep.jsondiff.core.handle.array.AbstractArrayJsonNeat 而且重写以下办法。

/**
* 比较目标
* @param expect 希望的json目标
* @param actual 实践的json目标
* @return 回来比较成果
* @throws IllegalAccessException 产生反常直接抛出
*/
JsonCompareResult detectDiff(JSONObject expect, JSONObject actual);

数组比较:

需求承继 me.codeleep.jsondiff.core.handle.object.AbstractObjectJsonNeat 而且重写以下办法。

  /**
 * 比较数组.调用进口。需求自己去分别调用 ignoreOrder 和  keepOrder。
 * @param expect 希望的json目标
 * @param actual 实践的json目标
 * @return 回来比较成果
 */
JsonCompareResult detectDiff(JSONArray expect, JSONArray actual);
// 疏忽次序的比较
JsonCompareResult ignoreOrder(JSONArray expect, JSONArray actual);
// 坚持次序比较
JsonCompareResult keepOrder(JSONArray expect, JSONArray actual);

根本类型比较:

根本类型指的是java根底类型的包装类型以及Number的完成类型。

需求承继 me.codeleep.jsondiff.core.handle.primitive.AbstractPrimitiveJsonNeat 而且重写以下办法。

   /**
     * 比较数组
     * @param expect 根底类型目标
     * @param actual 根底类型目标
     * @return 回来比较成果
     */
    JsonCompareResult detectDiff(Object expect, Object actual);

用户能够自己依据 travelPath 来决定运用何种自定义比较。三种比较器都回来 JsonCompareResult 目标作为当前节点的比较成果。关于JsonCompareResult目标。需求填入以下信息:

// 示例
JsonCompareResult result = new JsonCompareResult();
Defects defects = new Defects()
                  .setActual(actualDiffJson)
                  .setExpect(expectDiffJson)
                  .setTravelPath(nextTravelPath)
                  .setIllustrateTemplate(DATA_TYPE_INCONSISTENT, expectDiffJson.getClass().getName(), actualDiffJson.getClass().getName());
result.addDefects(defects);

假如遇到在自定义节点中,还需求运用体系自带的比较器时。

// 该值能够在上述三个笼统类中取得。但需求经自行处理
String abstractTravelPath = "root";
// 下一级是目标
TravelPath nextTravelPath = new TravelPath(abstractTravelPath, mappingKey);
// 下一级是数组
TravelPath nextTravelPath = new TravelPath(abstractTravelPath, expectIndex, actualIndex);
// 取得比较器
JsonDiffUtil.getJsonNeat(expectDiffJson, actualDiffJson, nextTravelPath);
// 履行比较取得成果
JsonCompareResult diff = jsonNeat.diff(expectDiffJson, actualDiffJson, nextTravelPath);
// 本级创立的 JsonCompareResult result 将下一级成果合并
this.result.mergeDefects(diff.getDefectsList());

能够运用上述代码获取体系自带的比较器。

自定义比较器值得注意的是: 从匹配到 travelPath 之后,依据不再接收比较操作。全部行为由用户自行定义。但东西依然预留默认的比较器给用户处理后续字段。这需求用户自行进行组合调用。

4.其他说明

前面说到东西简直能够支撑一切json成果的比照校验,而且发现差异。那它究竟能够支撑哪些呢,不知道是否契合你的需求呢?

  • 目标 ✅

    这是最简单的数据结构了,其中元素都以key-value构成,key是字符串,value能够是任何数据结构。

  • 数组 ✅

    支撑严厉次序比照和疏忽次序比照,能够细化数组元素的类型

    • 根本类型 ✅

    • 目标类型 ✅

      该类型在比照时,能够经过ignorePath参数进行元素是否进行比较,将不关心的元素疏忽掉。当然ignoreKey也能够,但其是大局收效

    • 数组类型 ✅

      元素也是数组,这样就形成了多维数组,东西理论上来说支撑n维数组的比照

    • 元素类型不一致 ✅

      数组中,类型或许包括前面三种类型,这时东西会依照类型分类进行匹配,最后找不到的元素再反馈给用户。

因为json结构在单个看来,就只有目标和数组两种类型,该东西完美支撑了一切类型。

四、其他

1. 沟通群

沟通群: 710435809

2. 测验用例

现在东西测验掩盖率现已到达80%。剩下测验用例正在补全中,彻底可用于生产环境

本文由博客一文多发平台 OpenWrite 发布!