ZeroTool Workbench

JSON 差异对比

比较两个 JSON 对象并可视化差异,输出 RFC 6902 JSON Patch 操作和彩色树形差异视图,完全在浏览器中运行。

100% 浏览器端运行 数据不离开你的设备 免费 · 无需注册

使用方法

  1. 将原始 JSON 粘贴到「修改前」面板。
  2. 将修改后的 JSON 粘贴到「修改后」面板。
  3. 点击比较执行差异对比。
  4. 在可视化差异中查看人类可读的变更。
  5. 复制 JSON Patch 用于你的应用程序或 API。

使用场景

  • 审查不同版本之间的 API 响应变更。
  • 为 PATCH HTTP 接口生成 patch 操作。
  • 审计配置文件变更。
  • 调试应用中意外的数据变更。

相关工具:JSON 格式化 — 在差异对比前格式化并验证 JSON。

示例

输入前:

{"name":"Alice","status":"pending","legacy_id":12345,"tags":["a","b","c"],"profile":{"age":30,"city":"Paris"}}

输入后(键顺序变了,status 改了,删除 legacy_id,新增 role,去掉第一个标签):

{"status":"active","name":"Alice","role":"admin","tags":["b","c"],"profile":{"age":31,"city":"Paris"}}

可视化差异:

~status: "pending" → "active"
−legacy_id: 12345
~tags.0: "a" → "b"
~tags.1: "b" → "c"
−tags.2: "c"
~profile.age: 30 → 31
+role: "admin"

JSON Patch:

[
  { "op": "replace", "path": "/status", "value": "active" },
  { "op": "remove", "path": "/legacy_id" },
  { "op": "replace", "path": "/tags/0", "value": "b" },
  { "op": "replace", "path": "/tags/1", "value": "c" },
  { "op": "remove", "path": "/tags/2" },
  { "op": "replace", "path": "/profile/age", "value": 31 },
  { "op": "add", "path": "/role", "value": "admin" }
]

键顺序不影响结果:name 换了位置但不会被报告。这个 patch 用 Python 的 jsonpatch 1.33 应用后得到「输入后」的文档:

import jsonpatch  # pip install jsonpatch

before = {"name": "Alice", "status": "pending", "legacy_id": 12345,
          "tags": ["a", "b", "c"], "profile": {"age": 30, "city": "Paris"}}
patch = jsonpatch.JsonPatch([...])  # paste the patch from the tool
after = patch.apply(before)

数组如何比较

数组按下标比较,而不是按内容。从 ["a","b","c"] 去掉第一个标签,会显示为两次替换加一次删除,而不是一条 remove /tags/0。patch 仍然正确,只是比需要的长。

同一个数组删除多个元素时,删除按下标从大到小写出。例如一张订单原有 3 个商品行、现在只剩 1 个且数量变了:

[
  { "op": "replace", "path": "/items/0/qty", "value": 3 },
  { "op": "remove", "path": "/items/2" },
  { "op": "remove", "path": "/items/1" }
]

执行 remove /items/2 后数组还剩两项,/items/1 仍然存在。若按从小到大的顺序,第二条删除会越过数组末尾;任一操作失败,整个 patch 即失败(RFC 6902 第 5 节)。超出较短数组长度的元素写成 remove(大下标在前)或 add(小下标在前)。

路径与 JSON Pointer 转义

patch 里的路径是 JSON Pointer(RFC 6901):~ 写成 ~0,/ 写成 ~1。{"a/b":1,"m~n":2,"list":[1,2,3]} → {"a/b":2,"list":[1,2,3,4],"x":null} 的结果:

[
  { "op": "replace", "path": "/a~1b", "value": 2 },
  { "op": "remove", "path": "/m~0n" },
  { "op": "add", "path": "/list/3", "value": 4 },
  { "op": "add", "path": "/x", "value": null }
]

两边根节点类型不同(如对象与数组)时,patch 替换整个文档,路径是空串 ""(RFC 6901 中 / 指向名为空字符串的成员):

[
  { "op": "replace", "path": "", "value": [1] }
]

可视化差异用点号连接键名(profile.age、list.3),只是方便阅读;代码里请使用 JSON Patch 中的路径。

限制

  • 不生成 move、copy 操作;改名的键显示为 remove 加 add。
  • 对象数组不会按 id 字段配对。
  • 两边都必须是合法 JSON,状态栏会指出哪一边解析失败,例如 前 JSON 解析错误:Expected double-quoted property name in JSON at position 7。
  • 数字按 JavaScript 解析后比较:1.0 与 1 相同,超过 2^53 的整数会丢精度。
  • 工具的测试把每个生成的 patch 用严格的 RFC 6902 实现(以及已安装时的 Python jsonpatch)应用到「前」,结果必须等于「后」,另跑 3,000 组随机文档。

FAQ

什么是 RFC 6902 JSON Patch?

RFC 6902 定义了一种 JSON 文档格式,用于表达对 JSON 文档的一系列操作。每个操作是一个包含 'op'(add/remove/replace/move/copy/test)、'path'(RFC 6901 JSON Pointer)和可选 'value' 的对象。

我的 JSON 数据会被发送到服务器吗?

不会。比较完全在浏览器中通过 JavaScript 完成,数据不会离开本机。

可以比较大型 JSON 文件吗?

可以,但包含数千个键或深层嵌套数组的超大对象可能需要片刻处理时间。所有计算均在浏览器内完成。

可视化差异是如何工作的?

工具执行深层递归比较。只在「After」中存在的键显示为添加(+);只在「Before」中存在的键显示为删除(−);值发生变化的键显示为修改(~),同时展示旧值和新值。

这与文本对比工具有什么不同?

文本对比工具逐行比较纯文本。JSON 差异工具理解 JSON 结构,对值进行语义比较而非文本比较,并输出机器可读的 JSON Patch 操作。