在数据驱动的现代世界中,JSON(JavaScript Object Notation)已成为最流行的数据交换格式之一。无论是在Web开发、API集成还是配置管理中,JSON文件的比较都是一个常见且重要的任务。然而在大文件场景,尤其是数据量达到 GB 级别,大多数的在线工具处理都会难以处理。我写了一个 JSON 大文件比较的开源项目,给大家提供一个便捷的方式处理这种应用场景。欢迎大家探讨交流思路。
项目地址:github.com/toddWang23/…
NPM 地址:www.npmjs.com/package/jso…
在线 Demo 地址:暂缺
使用方式
npm install json-file-comparator
或者命令的方式
npx json-file-comparator --reference=refpath --compare=comparepath --output=outputpath --size=10240
import {compareJSON2File} from "json-file-comparator";
import path from 'path'
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 被比较的文件
const comparePath = path.join(__dirname, './compare.json')
// 比较的基准文件
const refPath = path.join(__dirname, './reference.json')
// 输出的文件路径
const outPath = path.join(__dirname, './output.json')
compareJSON2File(refPath, comparePath, outPath)
在上面的例子中,对比 compare.json 和 reference.json 两个文件,将对象的不同之处写到 output.json 中。结果为对象类型的 JSON 格式,key 为差异节点 JSON Path 路径,value 为差异节点的具体不同。差异的类型分为:
- ADD:
compare.json中新增该节点 - REMOVED:
compare.json中该节点被删除 - MOVED:
reference.json中该节点被移动了 - VALUE_CHANGE:两个文件中,对应节点值有更改
- MOVED_CHANGE:两个文件中,对应节点值有更改,并且也移动了
示例输出:
{
"$.added-node": {
"type": "add",
"content": "newly added content, based on type various, it can be string/array/object/number"
},
"$.removed-node": {
"type": "removed",
"content": "removed content, based on type various, it can be string/array/object/number"
},
"$.moved-node": {
"type": "move",
"prevIndex": 0,
"changedIndex": 2
},
"$.value-change-node": {
"type": "value_change",
"prevValue": "previous content in reference file, based on type various, it can be string/array/object/number",
"changedValue": "updated content in compare file, based on type various, it can be string/array/object/number"
},
"$.move-change-node": {
"type": "move_change",
"prevIndex": 0,
"changedIndex": 2,
"prevValue": "previous content in reference file, based on type various, it can be string/array/object/number",
"changedValue": "updated content in compare file, based on type various, it can be string/array/object/number"
}
}
思路
传统的大文件处理思路通常采用流式处理,即每次只读取一个文件块,处理完毕后再读取下一个文件块。这种方法在处理线性数据时非常有效,但JSON文件的结构化特性要求我们采取更为精细的处理策略。在对比两个大型JSON文件时,我们需要同时读取两个文件,并对比相应的文件块。然而,JSON文件的变更,可能将一个节点从文件的头部移到尾部,因为流式处理通常只关注当前处理的文件块,而不会回溯或前瞻,这种流式对比的方式会很难识别到改动。
为了解决这个问题,我提出了一种策略:在对比过程中,我们需要捕获同一层级的所有节点信息。这样,即使节点跨越多个文件块,我们也能够准确地识别和对比它们。但如果JSON文件的结构较为简单,只有一层,我们则需要尽量减少节点信息的保留,以优化内存使用。
在这种情况下,我们需要保留以下信息:
- 下层节点的类型:用于校验节点是否符合预期的数据结构。
- 下层节点的开始与结束位置:用于继续对比后续的节点。
- 节点名称:用于识别和对比节点。
每一层的对比完成后,我们可以直接将结果写入文件,这样可以确保内存使用尽可能少。如果遇到变更的节点,并且该节点还有子节点,我们需要递归地进行对比,直到所有的子节点都被处理完毕。
项目规划
除了大文件的对比,in-memory 对象的对比也在计划之中。为了更好的让大家体验到这个类库,在线的 demo 网站也在筹备之中。另外,项目的性能优化也在持续的做,欢迎大家多多关注。