jq:递归重命名键

时间:2018-06-28 21:40:44

标签: jq

我有JSON,其结构如下所示。 “ groups”可以嵌套在“ items”下任意深度,因此我需要编写一个访问每个“ groups”和“ items”属性(或父对象)的表达式;静态路径不会。

{
    "groups": [{
        "name": "group1",
        "items": [
            {
                "name": "item1",
                "groups": [
                    {
                        "name": "nestedGroup1",
                        "items": [
                            {
                                "name": "nestedItem1",
                                "groups": []
                            },{
                                "name": "nestedItem2",
                                "groups": []
                        }]
                    }
                ]
            },
            {
                "name": "item2",
                "groups": []
            }
        ]
    }]
}

如何递归地将所有“组”重命名为“ newGroups”和“ items”为“ newItems”?如果您将其分解,我知道我需要在每个级别上创建新密钥并删除旧密钥,但是我很难表达出来。我已经尝试过类似

的几种变体
.. |= if type == "object" and has("groups") then . + { newGroups: .groups } else . end

执行第一步,但这会导致将顶层“ groups”值复制到“ newGroups”,而嵌套的“ groups”保持不变。然后,“组”中的其余值也将获得具有嵌套“组”的兄弟“ newGroups”,而没有“ newGroups”兄弟等。

从观察的角度来看,我的理解是首先评估..运算符生成的路径,因此永远不会遍历新添加的键。因此,然后我尝试了上述方法的变体,将..替换为recurse(无论是否带参数),都认为它将采用RHS产生的值并将其反馈回LHS,如下所示:

{ newGroups: .groups } | recurse |= if type == "object" and has("groups") then . + { newGroups: .groups } elif type == "object" and has("items") then . + { newItems: .items } else . end

结果更近,但仍然很远。递归类的工作原理,但仅适用于已存在的键;不会访问附加到对象的新对象。

我还尝试过弄混pathsgetpathsetpath来尝试以这种方式解决问题,但在那里也遇到了问题。请帮助我摆脱困境!

2 个答案:

答案 0 :(得分:1)

这在jq - FAQ中使用walk/1进行递归遍历时得到了很好的解释。我使用了jq-builtins页面上的walk/1的标准实现,因为默认情况下我没有该版本。

walk/1的文档开始-递归遍历输入JSON实体,将遇到的每个键k更改为(k|filter),其中filter是指定的过滤器,可以是任何jq表达式。

我在脚本中使用了以下过滤器

# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys_unsorted[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

def translate_keys(f):
  walk( if type == "object" then
            with_entries( if .key | contains("groups") then
                             .key |= "newgroups"
                          elif .key | contains("items") then
                             .key |= "newitems"
                          else
                             . end
                        )
        else
            . end
      );


translate_keys(.)

并将脚本运行为

jq -f script.jq json

产生所需的结果。

答案 1 :(得分:0)

如果问题要求仅在“组”键下的子树中对“项目”进行重命名,则需要在步行内进行步行:

walk(if type == "object" and has("groups")
     then .newGroups = .groups | del(.groups)
     | walk(if type == "object" and has("items")
            then .newItems = .items | del(.items)
            else . end)
     else . end)
相关问题