【问题标题】:Modify Nested JSON with jq使用 jq 修改嵌套 JSON
【发布时间】:2015-11-05 20:33:51
【问题描述】:

我正在尝试使用 bash/shell 脚本中的 jq <map> 函数修改嵌​​套的 JSON 对象;类似于 this blog entry 的东西,但试图将此处的示例调整为嵌套对象。

返回的JSON要修改如下:

{
  "name": "vendor-module",
  "dependencies": {
    "abc": {
      "from": "abc@2.4.0",
      "resolved": "https://some.special.url",
      "version": "2.4.0"
    },
    "acme": {
      "from": "acme@1.2.3",
      "resolved": "<CHANGE_THIS>",
      "version": "1.2.3"
    }
  }
}

这将是我的尝试:

modules="`node -pe 'JSON.parse(process.argv[1]).dependencies.$dependency' \
  "$(cat $wrapped)"`"
version="1.2.3"
resolved="some_url"

cat OLD.json | 
  jq 'to_entries | 
       map(if .dependencies[0].$module[0].from == "$module@$version"
          then . + {"resolved"}={"$resolved"}
          else . 
          end
         ) | 
      from_entries' > NEW.json

显然这不起作用。当我运行脚本时,NEW.json 被创建但没有修改或返回错误。如果我不针对嵌套对象(例如,"name": "vendor-module"),脚本将按预期工作。我确信有一种方法可以使用本机 bash 和 jq ..??任何帮助(通过适当的转义)将不胜感激。

更新:

感谢Charles Duffy's answer 的帮助,以及他使用sponge 的建议,适合我的解决方案是:

jq --arg mod "acme" --arg resolved "Some URL" \
   '.dependencies[$mod].resolved |= $resolved' \
   OLD.json | sponge OLD.json

【问题讨论】:

  • “acme”的“from”后面需要一个逗号,这样才能成为有效的 JSON,顺便说一句。
  • 等一下。您的输入变量中有modules——如果您已经知道需要修改acme,而不是abc,为什么还要查看fromversion
  • 错字-Thnx。我快速输入了这个作为示例 - 我不想用一堆不相关的代码污染问题。
  • 顺便说一句,我强烈建议逐步构建这种东西。如果您正在查看jq '.dependencies | to_entries' 的输出,那将向您显示您需要使用的数据结构类型(.value 下的内容和.dependencies 在当前范围内不可见)。
  • 另外,我强烈建议避免不必要地使用cat;以cat somefile | ... 提供输入的程序只得到一个不可逆的管道,而以... &lt;somefile 提供输入的程序有一个正确的、可查找的文件描述符;它既更高效(总是因为缺少管道,但如果你正在实现的内容允许更少的内存使用、更好的并行化或其他算法改进(当能够寻求),那么它更灵活)和更灵活。

标签: json bash sh jq


【解决方案1】:

如果您知道要更新的依赖项的名称,则可以对其进行索引。

$ jq --arg dep "$dep" --arg resolved "$resolved" \
    '.dependencies[$dep].resolved = $resolved' \
    OLD.json > NEW.json

否则,要根据名称(或其他属性)修改依赖项,请搜索依赖项并更新。

$ jq --arg version "$version" --arg resolved "$resolved" \
    '(.dependencies[] | select(.version == $version)).resolved = $resolved' \
    OLD.json > NEW.json

【讨论】:

  • 以这种方式使用select(限定变异操作)是一个非常好的技巧; +1。
【解决方案2】:

对于您现有的示例数据,以下就足够了:

jq --arg mod "acme" \
   --arg resolved "some_url" \
  '.dependencies[$mod].resolved=$resolved' \
  <in.json >out.json

...过滤from,相比之下:

jq --arg new_url "http://new.url/" \
   --arg target "acme@1.2.3" \
  '.dependencies=(.dependencies
                  | to_entries
                  | map(if(.value.from == $target)
                        then .value.resolved=$new_url
                        else . end)
                  | from_entries)' \
  <in.json >out.json

【讨论】:

  • 适用于以下编辑:jq --arg mod "acme" --arg resolved "some_url" '.dependencies[$mod].resolved=$resolved' OLD.json &gt; NEW.json 否则,jq 不知道如何处理它(例如,抛出:jq - 命令行 JSON 处理器 [1.3 版] 用法:jq [选项] [file...] 有关命令行选项的描述以及如何编写 jq 过滤器(以及您可能想要这样做的原因),请参见 jq 联机帮助页,或stedolan.github.com/jq 的在线文档)哈哈谢谢!
  • 明显的错误是在第二个--arg 之后缺少换行符延续;解决了这个问题后,答案中的代码在完全按原样复制粘贴时对我有用。
  • 这似乎更有效率:jq --arg mod "acme" --arg resolved "Some URL" '.dependencies[$mod].resolved |= [$resolved]' OLD.json | tee OLD.json 你怎么看?
  • 您的意思是tee?那很危险;管道的两端同时启动,因此tee 是否在jq 读取其输入之前打开其输出文件(并因此将其截断)只是运气问题。
  • ...如果您希望能够可靠地就地编辑文件而无需“写和重命名”,请查看 moreutils 的sponge 工具(在从标准输入获得 EOF 之前,它不会打开名称传递给输出的文件):joeyh.name/code/moreutils
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多