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

2023-02-11 16:51:10 +08:00
 molvqingtai

比如我有这样一个 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 库推荐也行

2959 次点击
所在节点    Node.js
23 条回复
justdoit123
2023-02-11 16:58:44 +08:00
预期的情况有两种:
1. 当你需要改动的时候,一定会产生 diff
2. 如果没有改动,则不能产生 diff

我的方案:直接用 JSON.parse/stringify 。你说的那种情况应该只有在原本的 JSON 字符串不是用 JSON.stringify 的情况下会产生。你可以先统一把已存在的文件全部重新格式化一遍,后续就不会产生 diff 。
bagel
2023-02-11 17:01:03 +08:00
key 排个序不就好了
molvqingtai
2023-02-11 17:07:55 +08:00
@mistkafka 我只想有改动的字段产生 diff ,因为一些流程的原因,没办法吧整个文件全部 parse 再 format 一遍
klo424
2023-02-11 17:10:55 +08:00
klo424
2023-02-11 17:12:00 +08:00
klo424
2023-02-11 17:13:20 +08:00
他的锚点不好用,重发[笑哭]

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

console.log(JSON.stringify({ uno: 1, dos: 2 }, null, "\t"));
/*
{
"uno": 1,
"dos": 2
}
*/
molvqingtai
2023-02-11 17:13:27 +08:00
@klo424 先 parse => stringify , 现在的问题是在 pasre 处就丢失格式信息了
tool2d
2023-02-11 17:18:25 +08:00
这种情况适合用 line parse 功能。

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

只序列化和反序列化这一小部分的文件内容,然后再拼装输出一个完整文件。相当于自己手动局部对象 JSON.parse/stringify ,而不是全局。
zictos
2023-02-11 17:19:55 +08:00
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 = `{
"pages": ["pages/index/index"],
"subpackages": [
{
"name": "A",
"pages": ["innerModule/pages/index/index"]
},
{
"name": "B",
"pages": ["innerModule/pages/index/index"]
}
]
}`;

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 了。
klo424
2023-02-11 17:24:57 +08:00
@molvqingtai #7 你好像没看懂我什么意思?你看#6 的代码,stringify 之前是没有格式的,加了后面的参数就有了格式。你手写的格式跟他一样就行了。
molvqingtai
2023-02-11 17:26:16 +08:00
@zictos 感谢,我试试这个代码,今天也用了 V 站上面免登录的 chatgpt ,问了半天也没问出你这样的结果,难道需要走什么流程
klo424
2023-02-11 17:29:17 +08:00
你试一下好吧,根本不需要什么库[笑哭]

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
}
}
klo424
2023-02-11 17:29:49 +08:00
v2 把格式过滤掉了,发不出格式。
molvqingtai
2023-02-11 17:33:32 +08:00
@klo424 感谢你的热情,但是我不能把已有的 JSON 都格式化一遍,而且输入的 object 格式也不一定和原 JSON 一致,总结就是想达到就像手动编辑一样的 diff 效果
molvqingtai
2023-02-11 17:33:57 +08:00
@klo424 v2 支持 markdown ....
klo424
2023-02-11 17:35:08 +08:00
评论不支持
klo424
2023-02-11 17:40:25 +08:00
那我觉得你只能用正则写了,其他的插件也会有自己的格式,都有相同的问题。
zictos
2023-02-11 17:43:38 +08:00
@molvqingtai #11 别人做的是第三方的,调用 api 的,api 的跟目前官方网页版不一样,一点都不智能一点都不好用。反正我试了基本很多答案都没法用,而官方网页版每次的回答都很智能很有针对性
lisongeee
2023-02-11 21:30:53 +08:00
<script src="https://gist.github.com/lisonge/51f6a7e6118a3412ecc226d20ea4fa0d.js"></script>

实现了找到替换的功能,使用的库是 acorn(解析 ast 获取位置) 和 magic-string(更新字符)
lisongeee
2023-02-11 21:39:08 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/915183

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX