Nodejs 如何修改 JSON string 中的值,只有改动部分产生 diff

发布于 1周前 作者 bupafengyu 来自 nodejs/Nestjs

Nodejs 如何修改 JSON string 中的值,只有改动部分产生 diff

比如我有这样一个 JSON

{
  "pages": ["pages/index/index"],
  "subpackages": [
    {
      "name": "A",
      "pages": ["innerModule/pages/index/index"]
    },
    {
      "name": "B",
      "pages": ["innerModule/pages/index/index"]
    }
  ]
}

需要实现一个函数找到 subpackages 下面 name === "xxx" 的 object ,然后替换当前的 object ,没有找到就 push 到 subpackages

使用 JSON.parse 很简单,但是会丢失格式信息,导致在 CI 中产生额外的 diff ,有什么很好处理这种场景的 AST 库推荐也行


24 回复

预期的情况有两种:
1. 当你需要改动的时候,一定会产生 diff
2. 如果没有改动,则不能产生 diff

我的方案:直接用 JSON.parse/stringify 。你说的那种情况应该只有在原本的 JSON 字符串不是用 JSON.stringify 的情况下会产生。你可以先统一把已存在的文件全部重新格式化一遍,后续就不会产生 diff 。


key 排个序不就好了

我只想有改动的字段产生 diff ,因为一些流程的原因,没办法吧整个文件全部 parse 再 format 一遍

他的锚点不好用,重发[笑哭]

console.log(JSON.stringify({ a: 2 }, null, " "));
/
{
“a”: 2
}
/

console.log(JSON.stringify({ uno: 1, dos: 2 }, null, “\t”));
/
{
“uno”: 1,
“dos”: 2
}
/

先 parse => stringify , 现在的问题是在 pasre 处就丢失格式信息了

这种情况适合用 line parse 功能。

先把{“name”: “A” … }这个对象片段包含的连续行提取出来,别的行号内容都保留下来。

只序列化和反序列化这一小部分的文件内容,然后再拼装输出一个完整文件。相当于自己手动局部对象 JSON.parse/stringify ,而不是全局。

chatgpt 的答案能参考吗?
推荐使用 jsonc-parser 库,它可以解析带注释的 JSON ,然后根据需要更新 JSON 对象并生成新的 JSON string 。下面是一个示例代码,可以实现题目要求的功能:

const { parseTree, findNodeAtLocation, applyEdits, modify } = require(“jsonc-parser”);

function updatePackage(jsonStr, packageName, packageData) {
const ast = parseTree(jsonStr);
const subpackagesNode = findNodeAtLocation(ast, [“subpackages”]);
const packageIndex = subpackagesNode.children.findIndex(
(child) => findNodeAtLocation(child, [“value”, “name”]).value === packageName
);
if (packageIndex !== -1) {
const packageNode = subpackagesNode.children[packageIndex];
const edits = modify(jsonStr, packageNode.offset, packageNode.length, JSON.stringify(packageData));
return applyEdits(jsonStr, edits);
} else {
const edits = modify(jsonStr, subpackagesNode.offset + subpackagesNode.length - 1, 0, ,${JSON.stringify(packageData)});
return applyEdits(jsonStr, edits);
}
}

const jsonStr = {<br> "pages": ["pages/index/index"],<br> "subpackages": [<br> {<br> "name": "A",<br> "pages": ["innerModule/pages/index/index"]<br> },<br> {<br> "name": "B",<br> "pages": ["innerModule/pages/index/index"]<br> }<br> ]<br>};

console.log(updatePackage(jsonStr, “C”, { name: “C”, pages: [“pages/C/index”] }));

在上面的代码中,updatePackage 函数接收一个 JSON string 、需要更新的包名和包数据。它首先使用 jsonc-parser 解析 JSON string ,然后在 AST 中查找 subpackages 节点。然后它在 subpackages 的子节点中查找与给定包名匹配的节点,如果找到了就将它替换为给定的包数据,否则就将包数据添加到 subpackages 的末尾。最后,它将生成的新 JSON string 返回。

由于使用 jsonc-parser 解析 JSON string 时可以保留格式信息,因此在 CI 中就不会产生额外的 diff 了。

#7 你好像没看懂我什么意思?你看#6 的代码,stringify 之前是没有格式的,加了后面的参数就有了格式。你手写的格式跟他一样就行了。

感谢,我试试这个代码,今天也用了 V 站上面免登录的 chatgpt ,问了半天也没问出你这样的结果,难道需要走什么流程

你试一下好吧,根本不需要什么库[笑哭]

console.log(JSON.stringify({uno:1,dos:[2,3],dns:{wf:{bbq:‘5’},aec:4}}, null, “\t”))

{
“uno”: 1,
“dos”: [
2,
3
],
“dns”: {
“wf”: {
“bbq”: “5”
},
“aec”: 4
}
}

v2 把格式过滤掉了,发不出格式。

感谢你的热情,但是我不能把已有的 JSON 都格式化一遍,而且输入的 object 格式也不一定和原 JSON 一致,总结就是想达到就像手动编辑一样的 diff 效果

v2 支持 markdown …

评论不支持

那我觉得你只能用正则写了,其他的插件也会有自己的格式,都有相同的问题。

#11 别人做的是第三方的,调用 api 的,api 的跟目前官方网页版不一样,一点都不智能一点都不好用。反正我试了基本很多答案都没法用,而官方网页版每次的回答都很智能很有针对性

<script src=“https://gist.github.com/lisonge/51f6a7e6118a3412ecc226d20ea4fa0d.js”></script>

实现了找到替换的功能,使用的库是 acorn(解析 ast 获取位置) 和 magic-string(更新字符)

试试 json patch

取决于你原 JSON 文本是如何产生的。

如果你可以控制原 JSON 文本的生成过程,你可以给字段名进行递归排序,然后利用 stringify 的第 2 、3 位参数(去看一下 stringify 的文档)来格式化生成。后续修改也用相同方式处理,就可以得到除了修改部分一致的 JSON 文本。这个概念通常被称为 json stable stringify ,有一些现成的库可以用。

如果你不能控制原 JSON 文本的生成过程,我没有想到什么好方法,可能只能用正则去匹配和修改文本中要改的那部分了。

感谢大家回复,已使用 #9 的 chatgpt 答案,改吧改吧解决了

在 Node.js 中修改 JSON 字符串中的值并生成 diff,你可以使用 JSON.parse 将 JSON 字符串转换为 JavaScript 对象,然后修改对象的值,再用 JSON.stringify 转换回字符串。为了生成 diff,可以使用 json-diff 库。

首先,确保你已经安装了 json-diff 库:

npm install json-diff

以下是一个示例代码,展示如何修改 JSON 字符串中的值并生成 diff:

const jsonDiff = require('json-diff');

// 原始 JSON 字符串
let jsonStr = '{"name": "Alice", "age": 25, "city": "New York"}';
let jsonObject = JSON.parse(jsonStr);

// 修改值
jsonObject.age = 26;

// 转换回 JSON 字符串
let newJsonStr = JSON.stringify(jsonObject);

// 生成 diff
let diff = jsonDiff.diffString(jsonStr, newJsonStr);

console.log('Original JSON:', jsonStr);
console.log('Modified JSON:', newJsonStr);
console.log('Diff:', diff);

运行上述代码后,输出将显示原始 JSON、修改后的 JSON 以及 diff。diff 显示了哪些部分被修改了,比如:

Diff: {
  "age": {
    "-": 25,
    "+": 26
  }
}

这样,你就可以清晰地看到 JSON 字符串中哪些值被修改了,并且只有改动的部分产生了 diff。

回到顶部