正在加载,请稍候…

JSON Diff:如何比较两个 JSON 对象并找出差异

学习如何在线比较 JSON 对象,找出新增、删除和修改的键。涵盖 API 响应比较、配置漂移检测和调试等常见用例。

JSON Diff:如何比较两个 JSON 对象并找出差异

什么是 JSON Diff?

JSON diff 是对两个 JSON 对象进行比较,识别出每个被添加、删除或值发生变化的键。与文本 diff(逐行比较)不同,JSON diff 理解结构:它通过键匹配对象,通过位置(或键字段)匹配数组,并在语义层面而非字符层面报告变化。

这使得 JSON diff 对开发者来说比将两个 JSON 文件输入 diff 有用得多。如果你只是重新格式化了缩进,文本 diff 会显示整个对象发生了变化。而 JSON diff 忽略格式,专注于数据。

JSON Diff:如何比较两个 JSON 对象并找出差异 插图

为什么开发者需要 JSON Diff

调试 API 变更

你对后端端点进行了更改,需要确认响应结构完全符合前端预期。比较变更前后的响应,查看发生了什么变化。

检测配置漂移

两个环境(预发布和生产)应具有相同的配置,但出了问题。JSON diff 立即揭示哪些键出现了分歧以及差异有多大。

代码审查

拉取请求更改了 JSON schema、fixture 文件或 package-lock.json。语义 diff 显示有意义的更改,而不会因重新格式化而产生噪音。

回归测试

将预期的 API 响应存储为 JSON fixture。代码更改后,将新响应与 fixture 进行 diff,以捕获非预期的更改。

审计状态变更

在 Redux 或类似的状态管理模式中,记录状态快照并进行 diff,以准确了解哪些操作发生了变化。

JSON Diff:如何比较两个 JSON 对象并找出差异 插图

差异类型

JSON diff 通常报告三种类型的变更:

新增键

键存在于第二个(新)对象中,但不存在于第一个(旧)对象中:

// 旧
{ "name": "Alice", "role": "admin" }

// 新
{ "name": "Alice", "role": "admin", "email": "alice@example.com" }

// Diff: + "email": "alice@example.com"

删除键

键存在于第一个(旧)对象中,但不存在于第二个(新)对象中:

// 旧
{ "name": "Alice", "legacy_id": 12345 }

// 新
{ "name": "Alice" }

// Diff: - "legacy_id": 12345

修改值

键在两个对象中都存在,但值不同:

// 旧
{ "status": "pending", "retries": 0 }

// 新
{ "status": "complete", "retries": 3 }

// Diff: ~ "status": "pending" → "complete"
//       ~ "retries": 0 → 3

嵌套对象 Diff

一个好的 JSON diff 会递归比较嵌套对象:

// 旧
{
  "user": {
    "name": "Alice",
    "address": { "city": "Paris", "country": "FR" }
  }
}

// 新
{
  "user": {
    "name": "Alice",
    "address": { "city": "Lyon", "country": "FR" }
  }
}

// Diff: ~ user.address.city: "Paris" → "Lyon"

报告完整路径(user.address.city)而不仅仅是键名(city)对于大型、深度嵌套的对象至关重要。

数组 Diff 的挑战

数组比对象更难 diff,因为元素没有固有的标识符。两种策略:

JSON Diff:如何比较两个 JSON 对象并找出差异 插图

基于索引的 Diff

比较相同数组索引处的元素。简单,但对重新排序敏感:如果在开头插入一个元素,之后的所有内容都会显示为“已更改”。

基于键的 Diff

如果数组元素是具有已知标识符字段(如 idslug)的对象,则先通过该键匹配元素,然后再进行比较。这可以正确处理重新排序、插入和删除。

实际示例

比较 API 响应

// 在变更前后获取数据
const before = await fetch('/api/users/1').then(r => r.json());
// ... 进行你的更改 ...
const after = await fetch('/api/users/1').then(r => r.json());

// 将两者粘贴到 JSON Diff 工具中查看变化

比较 package.json 文件

升级依赖时,对旧的和新的 package.json 进行 diff,可以确认哪些包的版本发生了变化以及添加了哪些新依赖。

比较配置文件

环境通常将配置存储为 JSON。对 config.staging.jsonconfig.production.json 进行 diff,可以找出任何应该匹配但不匹配的值。

JSON Diff 与文本 Diff 对比

特性 JSON Diff 文本 Diff
理解结构
忽略格式
处理键重新排序
无需 JSON 知识即可工作
适用于非 JSON 内容
显示变更键的路径

对于 JSON 内容,始终使用语义化的 JSON diff。将文本 diff 保留给非结构化内容。

如何阅读 Diff 输出

标准 diff 符号:

  • +(绿色):在新版本中新增
  • -(红色):从旧版本中删除
  • ~ 或无符号(黄色):值已更改
  • 无变化:键在两个版本中都存在且值相同(通常隐藏以减少噪音)

比较大型 JSON 对象时,使用“隐藏未更改的键”选项,只关注不同的部分。

→ 使用 JSON Diff 工具,将两个 JSON 对象并排粘贴,即可获得即时、结构化的比较结果,突出显示所有变化。