json对比工具怎么用?键序不同的两份 JSON 怎么比才不出假差异
两份 JSON 明明数据一样,diff 出来却满屏红绿,九成是键序和缩进造成的假差异。用 json对比工具 的正确做法是先把两侧解析成结构、按字段名对齐再比,而不是拿两段文本逐行对。
先说结论
JSON 对象是无序的键值集合,键的前后位置不属于数据本身,键序不同不构成差异。
逐行文本对比(编辑器的 compare、git diff)找的是相同的行,键序一变行就错位,整段会被标成「删除 + 新增」,于是你看到一堆并不存在的改动。要判断两份 JSON 到底差在哪,必须走结构对比:解析、排序键名、按字段名递归对齐。
数组是另一回事。数组有序,按下标逐个比是符合语义的;但如果这个数组装的是「一批结果」,顺序本身没有业务含义,那就得先排序再比,否则每换一次返回顺序就报一遍差异。
为什么会这样
行级 diff 算法找的是公共行,不是公共字段。 文本对比常用的是最长公共子序列一类的算法,它把文件切成行,找出两边都存在的行块。一份 JSON 里有 "name"、"age"、"city" 三个键,左边顺序是 name、age、city,右边是 age、city、name,三行文本完全一样,只是位置换了,算法匹配出来的公共块立刻碎掉,输出的结果就是「左边删了三行、右边加了三行」。数据一个字没变,报告里却是六处改动。
JSON 规范本身就不保证对象有序。 RFC 8259 明确写了对象是无序的键值集合,字段的出现顺序不属于语义。解析器读进来通常存成哈希表或映射,写出时按自己的规则排序。这也解释了一个常见现象:同一份数据从 Java 服务、Go 服务、Node 服务分别返回,字段顺序往往各不相同,但它们是同一份数据。
数组的语义是位置,所以按下标比是对的。 ["a","b"] 和 ["b","a"] 是两个不同的数组,这一点没有争议。问题出在「数组装的是集合」的场景:分页查询返回的用户列表,数据库没有 ORDER BY 时每次顺序都可能变,按下标比对就会把这些波动当成差异,噪音盖住真正的改动。
还有一层是值的等价性。 数字 1 与 1.0 在多数语言里是同一个数,序列化回文本却不同;字符串里的 \u4e2d 和 中 解析后完全相等;null 和「这个键不存在」是两件事,但在很多 diff 里都显示为空;重复键只保留最后一个,前面的被静默丢弃。这些都不是工具算错,是「比字节」和「比语义」的区别。
所以真正要决定的不是用哪个工具,而是这次要比什么:比接口契约,就比结构的增减;比数据是否一致,就比值;比发布包有没有变,才轮到比字节。
具体怎么做
- 先把两份原始返回各存一份。 用
curl -s url > before.json这样的方式落盘,别只留在浏览器请求记录里。后面确认「是环境差异还是真改坏了」,全靠这份原始件。
- 各自格式化一遍,消掉缩进和空白的噪声。 拿在线 JSON 格式化贴进去,它会补全键名的双引号、统一缩进,还能切换正序与倒序。这一步做完,两份文件的排版基准就一致了。
- 确认不要用「压缩成单行」的版本去比。 压成一行之后,行级 diff 完全失去意义,整份文件会被当成一行处理,红了就是全红。
- 打开 JSON 对比,左旧右新粘贴。 左边放改动前的、右边放改动后的,方向不要反,否则新增和删除的颜色会整体互换。贴错了就点「交换左右」,不用重新复制。
- 看两侧键名有没有排到同一顺序。 页面默认按键名正序排列,也可以切到倒序,两侧会同步。键名比较是区分大小写的,
userId和userid在 JSON 里是两个不同的键,这类差异要当结构问题处理,不是笔误。
- 按颜色分层处理,先结构后取值。 红色表示右侧缺少这个字段,绿色表示右侧新增,橙色表示两侧都有但值不同。先清红绿——那是结构变动,往往对应一次字段增删;橙色属于取值变化,逐条核对即可。
- 看统计数字,再复制差异清单。 新增、删除、修改三个计数能快速判断这次改动量级:只有橙色的通常是数据变化,出现红绿就说明契约动了。清单可以直接复制进工单或代码评审评论,比截图清楚。
- 数组差异单独看一眼。 如果差异全部集中在某个数组内,先确认这个数组有没有业务顺序。没顺序的,把元素按主键(
id之类)排一遍再比,噪音会立刻消失。
- 涉敏数据先脱敏再比。 这两个工具都在浏览器本地完成比对,输入不会发到服务器;但返回里带 token、手机号、密钥的,仍然建议替换成占位符再贴,截图也不要随手发群里。
参数对照
| 差异类型 | 逐行文本对比的表现 | 按字段名对比的表现 | 处理建议 |
|---|---|---|---|
| 对象内字段换了位置 | 整段报删除 + 新增 | 通常零差异 | 无视,属正常序列化差异 |
| 缩进、空格、换行不同 | 每一行都不同 | 零差异 | 先格式化再比 |
| 某个字段的值变了 | 报差异,但定位在行 | 报橙色,定位到具体字段 | 逐字段核对取值 |
| 右侧缺字段 / 多字段 | 报差异且位置错乱 | 红 / 绿标在父级下 | 按结构变动处理,同步契约 |
| 数组元素顺序不同 | 报差异 | 报差异(按下标比) | 先按业务主键排序 |
类型不同(1 与 "1") | 报差异 | 报差异,同色标出 | 先确认是不是序列化 bug |
表格里的判断只解决「差异长什么样」,不解决「哪个是对的」。哪份符合契约,要回去看接口文档或 JSON Schema,diff 工具不承担这件事。
常见误区
以为没有红绿就是完全一致。 工具比的是两侧都存在的字段,一侧整体缺失的子树只会标在父级上。父级字段整个消失时,下面十几个子字段不会逐个冒红,看统计里的「删除」计数更靠谱。
以为 diff 能替代 schema 校验。 差异对比只回答「两份哪里不同」,不回答「这份合不合规范」。两份都错的 JSON 比出来可能零差异,那不代表它是对的。
以为键序不同一定要修。 对象键序不是契约的一部分,绝大多数解析器不关心。真正该在意顺序的是数组,而数组要不要在意顺序,取决于接口有没有承诺排序。
以为本地处理就等于绝对安全。 本地处理只是说内容不上传服务器,浏览器扩展、输入法云同步、剪贴板历史仍然可能把内容带走。工具这一环是干净的,环节之外不是。
常见问题
问:json对比工具会把接口数据传到服务器吗? 取决于实现。标了本地处理的工具,比对在浏览器里跑完,输入不发到后端,断网后依然能对比——这也是一种验证办法。含密钥、手机号、身份证号的返回,建议先替换成占位符再贴,无论用哪个网站。
问:两份 JSON 键顺序不一样,diff 出来全是红绿怎么办? 说明用的是逐行文本对比,换掉即可。按字段名对齐的结构对比不受键序影响,两侧键名会自动排到同一顺序再逐字段比,键序差异直接归零。
问:数组里对象的顺序变了,算不算差异? 看接口有没有承诺顺序。按下标比对一定报差异;如果业务上顺序无意义(分页结果、批量查询),先按主键排序再比,否则每次返回顺序波动都会被记成改动。
问:为什么 1 和 1.0 会被标成不同? 因为它们在序列化后的文本里不一样。比较时若按原始字面量走,就会报差异;按数值走则相等。遇到这种差异先查两侧的序列化库:Java 的 BigDecimal、Python 的 float、Go 的 float64 输出格式各不相同。
问:大整数 ID 比对时对不上是怎么回事? 超过 2^53 的整数在 JavaScript 里会丢精度,9007199254740993 读进来会变成 ...992。这类 ID 应当以字符串传输,比对前先确认两侧都没被截断,否则差异是精度造成的,不是数据本身。
要现成的话,直接用 JSON 对比 把两份 JSON 左右贴进去,结构、颜色和差异清单一次看完。