我有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
结果更近,但仍然很远。递归类的工作原理,但仅适用于已存在的键;不会访问附加到对象的新对象。
我还尝试过弄混paths
,getpath
和setpath
来尝试以这种方式解决问题,但在那里也遇到了问题。请帮助我摆脱困境!
答案 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)