使用 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"],""]
...

reducesetpath 可以用来转换回 带有过滤器的原始形式,例如:

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