JSON 对比

按结构而非按行对比两份 JSON 文档,列出新增、删除和修改的路径,可选择忽略数组顺序。全程在浏览器内运行。

按结构对比,而不是按行。 粘贴两份文档,即可得到逐路径的新增、删除、修改报告。由于比较的是解析后的值,重新排版或调整键顺序都不会被算作差异。

差异

为什么文本 diff 不适合 JSON

行级 diff 看到的是排版,不是含义

标准的行级 diff 把 JSON 当作散文处理。把文件的缩进从 2 空格改成 4 空格,每一行都会被标记为已更改;把某个键从对象顶部挪到底部,会得到一处删除加一处新增,尽管解析后的值一模一样;把一个长数组折成一行,整段都会亮起。

结构化 diff 会先解析两边,再遍历得到的值。比较开始时排版、键顺序和空白早已不复存在,因此能报告出来的只可能是数据本身的真实差异。这正是它的输出足够短、真的能读完的原因。

用路径而不是行号

这里的每一处差异都对应一个路径,例如 user.roles[2]meta.updated。路径不会因为文档别处的改动而失效,行号会。所以你可以把同一份报告贴进工单或代码评审,一周后它依然指向正确的位置。

三种操作恰好对应树上可能发生的三件事:左边有而右边没有的键是删除,反过来是新增,两边都有但值不同的键是修改。类型变化——某个字段原本是数字 1,现在成了字符串 "1"——同样被报告为修改,而且很值得留意,因为这类问题是下游 bug 的常见来源,任何 schema 校验都未必能拦住。

数组与顺序问题

数组是结构化对比中最需要取舍的地方。默认采用按位置比较:左边的下标 0 与右边的下标 0 相比。对于步骤序列这类有序数据这是正确的,但对于本质是集合的列表就会得出误导性的结果——只要元素换个顺序,每个位置看上去都变了。

「忽略数组顺序」选项会在比较前递归地对两边的数组排序,使其规范化,于是重排过的列表会被判为相等。标签列表、权限集合、没有保证顺序的查询结果都适合打开它。而只要元素位置本身有含义,就应该关掉——因为打开之后,真正的顺序错误会变得完全不可见。

开源说明:使用原生 JavaScript 实现,不依赖第三方库。

常见问题

它和普通的文本 diff 有什么不同?
文本 diff 比较的是行,所以重新缩进或调换键顺序都会显示为变更。本工具先解析两边,因此只报告数据上的真实差异。
+、- 和 ~ 分别代表什么?
加号表示只在修改后文档中存在的路径,减号表示只在原始文档中存在,波浪号表示两边都有但取值不同。
为什么调换顺序的数组被判为已修改?
默认按位置逐一比较数组,这对有序数据是正确的。若要按无序集合比较,请打开「忽略数组顺序」。
对象里的键顺序有影响吗?
没有。对象按键比较,因此在对象内部移动字段永远不会产生差异。只有数组元素的位置才对顺序敏感。
数字变成字符串会被标记出来吗?
会。类型变化会被报告为修改,这是刻意的:1 和 "1" 在下游的行为差别很大,不标出来极易漏掉。
我的文档会被发送到服务器吗?
不会。两边的解析与比较都在浏览器内完成,不上传、不记录、不存储。