使用 jq 从嵌套数组值中仅获取所需的属性
Getting only desired properties from nested array values with jq
我最终想要的结构是:
{
"catalog": [
{
"name": "X",
"catalog": [
{ "name": "Y", "uniqueId": "Z" },
{ "name": "Q", "uniqueId": "B" }
]
}
]
}
这是现有结构的样子,除了每个级别 (许多 个其他属性 (https://gist.github.com/ajcrites/e0e0ca4ca3a08ff2dc401ec872e6094c)。我只是想过滤掉那些并得到一个看起来特别像这样的 JSON 格式。
我开始使用:jq '.catalog'
,但是这个 returns 只是数组。我仍然想要 catalog
属性 名称。我可以用 jq '{catalog: .catalog[]}
来做到这一点,但这会单独打印出每个目录对象,这使得整个输出无效 JSON。我仍然希望属性在数组中。有没有办法使用 jq 过滤数组中特定的 属性 键值?
以下将给定的输入转换为所需的输出,可能正是您想要的:
{catalog}
| .catalog |= map( {name, catalog} )
| .catalog[].catalog |= map( {name, uniqueId} )
| .catalog |= .[0:1]
但是,我不清楚这是否真的是您想要的,因为您没有讨论给定 JSON 输入中的重复。所以也许您真的不想要上面的最后一行,或者您希望以其他方式处理重复项,或者....
无论如何,这里保持简单的技巧是使用 |=
。
另一种方法是使用 del
删除不需要的属性(而不是选择您想要的属性),但在目前的情况下,这(充其量)是乏味的。
您可以先使用 tostream 来转换您的 sample.json
进入 [path, value] 数组流,如 运行
所示
jq -c tostream sample.json
这将生成
[["catalog",0,"catalog",0,"name"],"Y"]
[["catalog",0,"catalog",0,"prop11"],""]
[["catalog",0,"catalog",0,"uniqueId"],"Z"]
[["catalog",0,"catalog",0,"uniqueId"]]
[["catalog",0,"catalog",1,"name"],"Y"]
[["catalog",0,"catalog",1,"prop11"],""]
...
reduce 和 setpath 可以用来转换回
带有过滤器的原始形式,例如:
reduce (tostream|select(length==2)) as [$p,$v] (
{};
setpath($p;$v)
)
添加条件可以很容易地忽略任何级别的属性。
例如,以下删除以 "prop" 开头的叶属性:
reduce (tostream|select(length==2)) as [$p,$v] (
{};
if $p[-1]|startswith("prop")
then .
else setpath($p;$v)
end
)
用你的 sample.json
这会产生
{
"catalog": [
{
"catalog": [
{
"name": "Y",
"uniqueId": "Z"
},
{
"name": "Y",
"uniqueId": "Z"
}
],
"name": "X"
},
{
"catalog": [
{
"name": "Y",
"uniqueId": "Z"
},
{
"name": "Y",
"uniqueId": "Z"
}
],
"name": "X"
}
]
}
如果目标是删除某些属性,则可以使用 walk/1
来实现。例如,删除名称以 "prop":
开头的属性
walk(if type == "object"
then with_entries(select(.key|startswith("prop") | not))
else . end)
如果重点是保留某些属性,则同样的方法也适用,例如:
walk(if type == "object"
then with_entries(select(.key == "name" or .key == "uniqueId" or .key == "catalog"))
else . end)
您可以构建一个文件,其中包含要保留的 json(表示为数组)的路径。然后过滤掉不适合这些路径的值。
paths.json:
["catalog","name"]
["catalog","catalog","name"]
["catalog","catalog","uniqueId"]
然后根据路径过滤值。使用流是实现此目的的好方法,因为它使您可以直接访问这些路径:
$ jq --slurpfile paths paths.json '
def keep_path($path): any($paths[]; . == [$path[] | select(strings)]);
fromstream(tostream | select(length == 1 or keep_path(.[0])))
' input.json
我最终想要的结构是:
{
"catalog": [
{
"name": "X",
"catalog": [
{ "name": "Y", "uniqueId": "Z" },
{ "name": "Q", "uniqueId": "B" }
]
}
]
}
这是现有结构的样子,除了每个级别 (许多 个其他属性 (https://gist.github.com/ajcrites/e0e0ca4ca3a08ff2dc401ec872e6094c)。我只是想过滤掉那些并得到一个看起来特别像这样的 JSON 格式。
我开始使用:jq '.catalog'
,但是这个 returns 只是数组。我仍然想要 catalog
属性 名称。我可以用 jq '{catalog: .catalog[]}
来做到这一点,但这会单独打印出每个目录对象,这使得整个输出无效 JSON。我仍然希望属性在数组中。有没有办法使用 jq 过滤数组中特定的 属性 键值?
以下将给定的输入转换为所需的输出,可能正是您想要的:
{catalog}
| .catalog |= map( {name, catalog} )
| .catalog[].catalog |= map( {name, uniqueId} )
| .catalog |= .[0:1]
但是,我不清楚这是否真的是您想要的,因为您没有讨论给定 JSON 输入中的重复。所以也许您真的不想要上面的最后一行,或者您希望以其他方式处理重复项,或者....
无论如何,这里保持简单的技巧是使用 |=
。
另一种方法是使用 del
删除不需要的属性(而不是选择您想要的属性),但在目前的情况下,这(充其量)是乏味的。
您可以先使用 tostream 来转换您的 sample.json
进入 [path, value] 数组流,如 运行
jq -c tostream sample.json
这将生成
[["catalog",0,"catalog",0,"name"],"Y"]
[["catalog",0,"catalog",0,"prop11"],""]
[["catalog",0,"catalog",0,"uniqueId"],"Z"]
[["catalog",0,"catalog",0,"uniqueId"]]
[["catalog",0,"catalog",1,"name"],"Y"]
[["catalog",0,"catalog",1,"prop11"],""]
...
reduce 和 setpath 可以用来转换回 带有过滤器的原始形式,例如:
reduce (tostream|select(length==2)) as [$p,$v] (
{};
setpath($p;$v)
)
添加条件可以很容易地忽略任何级别的属性。 例如,以下删除以 "prop" 开头的叶属性:
reduce (tostream|select(length==2)) as [$p,$v] (
{};
if $p[-1]|startswith("prop")
then .
else setpath($p;$v)
end
)
用你的 sample.json
这会产生
{
"catalog": [
{
"catalog": [
{
"name": "Y",
"uniqueId": "Z"
},
{
"name": "Y",
"uniqueId": "Z"
}
],
"name": "X"
},
{
"catalog": [
{
"name": "Y",
"uniqueId": "Z"
},
{
"name": "Y",
"uniqueId": "Z"
}
],
"name": "X"
}
]
}
如果目标是删除某些属性,则可以使用 walk/1
来实现。例如,删除名称以 "prop":
walk(if type == "object"
then with_entries(select(.key|startswith("prop") | not))
else . end)
如果重点是保留某些属性,则同样的方法也适用,例如:
walk(if type == "object"
then with_entries(select(.key == "name" or .key == "uniqueId" or .key == "catalog"))
else . end)
您可以构建一个文件,其中包含要保留的 json(表示为数组)的路径。然后过滤掉不适合这些路径的值。
paths.json:
["catalog","name"]
["catalog","catalog","name"]
["catalog","catalog","uniqueId"]
然后根据路径过滤值。使用流是实现此目的的好方法,因为它使您可以直接访问这些路径:
$ jq --slurpfile paths paths.json '
def keep_path($path): any($paths[]; . == [$path[] | select(strings)]);
fromstream(tostream | select(length == 1 or keep_path(.[0])))
' input.json