使用jq基于条件更新JSON

时间:2017-02-21 17:21:07

标签: json jq

假设我有一个如下所示的JSON文件:

{
  "name": "tom",
  "scripts": {
    "webpack": "webpack --progress --colors --watch"
  },
  "dependencies": {
  },
  "devDependencies": {
    "webpack": "^2.2.1"
  }
}

我希望能够按名称指定包,在dependenciesdevDependencies中找到相应的条目并更新值。

我最接近的是:

$ jq --arg p webpack --arg v 1.2.3 'to_entries | map(
   if (.value[$p]? | startswith("^")?) then 
      .value[$p] = $v
   else . 
   end
) | from_entries' file.json

更新值但会删除dependenciesname属性:

{
  "scripts": {
    "webpack": "webpack --progress --colors --watch"
  },
  "devDependencies": {
    "webpack": "1.2.3"
  }
}

如何更新所需的值而不影响原始JSON中的其他属性?

2 个答案:

答案 0 :(得分:1)

请改为尝试:

$ jq --arg p webpack --arg v 1.2.3 '
def update_package($package; $version):
    if has($package) then .[$package] = $version
    else . end;
.dependencies |= update_package($p; $v)
    | .devDependencies |= update_package($p; $v)' project.json

update_package/2只会在依赖对象实际引用给定项目时才更新它,否则它将保持不变。同时适用于dependenciesdevDependencies

原始过滤器删除dependencies对象的问题在于,由于项目不存在,因此没有相应的更新值。通过对不存在的值应用startswith,它会导致错误。该错误反过来被忽略并有效地跳过了地图中的相应项目,从而失去了该属性。

要快速修复以确保不会发生这种情况,请不要忽略该错误,在属性不存在时提供备用值。

.value[$p]?      | startswith("^")? # bad
.value[$p] // "" | startswith("^")  # better

答案 1 :(得分:1)

使用您的方法,最好使用map_values:

map_values(if type == "object" 
           then with_entries( if .key == $p and (.value | startswith("^")?) 
                              then .value = $v else . end )
           else . end)

您可能还想考虑使用walk/1

walk(if type == "object" and has($p) and (.[$p]|startswith("^"))
     then .[$p] = $v else . end)

使用walk/1的解决方案将递归检查JSON实体。如果您的jq没有walk,则可以通过Google搜索轻松找到其在jq中的定义。

使用/ 2

时的强大解决方案
def when(p;q): if p//false then q else . end;

map_values( when(type == "object";
                 with_entries( when( .key == $p and (.value | startswith("^")?);
                                     .value = $v) )) )