停止使用 Text Diff 处理 JSON:比较对象的更好方法
Source: Dev.to
问题:文本 vs. 语义
标准的 diff 算法(比如 Myers 差分算法)是线性的。它们比较字符或行的序列,不理解结构。
场景 A(压缩)
如果把一个压缩的 JSON 字符串和一个“美化”后的版本进行比较,即使数据完全相同,也会把每一行都标记为变化。
场景 B(键顺序)
在 JSON 中,对象的键是无序的。然而,JSON.stringify() 会生成键顺序固定的字符串。如果你的后端语言(如 Python 或 Go)在序列化 map 时使用随机的哈希种子,那么每次生成的 JSON 键顺序可能都会不同。
解决方案:语义比较
为了解决这个问题,不能只比较字符串,而必须比较对象本身。
我们在 Diff Guru 提供的工具在真正进行比较之前会执行三步:
- 验证 – 解析输入字符串,确保它是合法的 JSON。如果你漏掉了逗号,我们会精确指出位置。
- 规范化(深度排序) – 这就是秘密武器。我们递归遍历对象,并按字母顺序对每个键进行排序。
- 格式化 – 使用统一的 4 空格缩进对已排序的对象进行美化输出。
只有在完成上述步骤后,才会运行 diff 算法。
工作原理(实现方式)
下面是我们用来确保 { "b": 2, "a": 1 } 在比较前会变成 { "a": 1, "b": 2 } 的简化 TypeScript 代码:
const sortDeep = (o: any): any => {
// If it's not an object (or is null), return it as is
if (o === null || typeof o !== 'object') {
return o;
}
// If it's an array, map over the items recursively
// (Note: We usually don't sort arrays, as array order often matters!)
if (Array.isArray(o)) {
return o.map(sortDeep);
}
// It's an object: get keys, sort them, and rebuild the object
return Object.keys(o).sort().reduce((acc: any, key) => {
acc[key] = sortDeep(o[key]);
return acc;
}, {});
};
通过这段预处理函数,我们过滤掉了由格式和键顺序导致的“噪音”,只保留实际的数据变化。
隐私优先(仅客户端)
在构建此工具时,我意识到开发者经常会把敏感数据——API 密钥、用户记录、AWS 配置——粘贴到 diff 工具中。
大多数在线工具会把你的数据发送到后端进行处理。Diff Guru 不会这样做。
我们采用 100 % 客户端架构。排序、格式化以及 diff 逻辑(使用 diff‑match‑patch)全部在浏览器的 JavaScript 引擎中运行。你完全可以打开页面后关闭 Wi‑Fi,工具仍能正常工作。你的数据永远不会离开你的机器。
试一试
如果你已经厌倦了手动寻找缺失的逗号或费劲辨别两个庞大 JSON 为什么会被判定为不同,欢迎尝试一下。
免费使用,无需注册,且尊重你的数据隐私。使用后欢迎在评论区告诉我你的感受!