ZeroTool Workbench
JSON 差异对比
比较两个 JSON 对象并可视化差异,输出 RFC 6902 JSON Patch 操作和彩色树形差异视图,完全在浏览器中运行。
使用方法
- 将原始 JSON 粘贴到「修改前」面板。
- 将修改后的 JSON 粘贴到「修改后」面板。
- 点击比较执行差异对比。
- 在可视化差异中查看人类可读的变更。
- 复制 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 操作。