jq select 可以通过过滤器同时保留完整的输入上下文吗?

Can jq select via a filter while retaining the full input context?

在 jq 中是否有通用机制 select JSON 文件的任意元素,但 return 这些元素的完整结构上下文?例如,如果我有以下内容:

{
  "foo": {
    "one": true,
    "two": false,
    "three": {
      "hello": "world"
    },
    "four": true
  },
  "bar": [
    1,
    4,
    5
  ],
  "baz": true
}

使用过滤器 .foo,.baz 通常会导致:

{
  "one": true,
  "two": false,
  "three": {
    "hello": "world"
  },
  "four": true
}
true

但我想要的是:

{
  "foo": {
    "one": true,
    "two": false,
    "three": {
      "hello": "world"
    },
    "four": true
  },
  "baz": true
}

我可以使用 select 专门为给定的过滤器解决这个问题,但我想要一些通用的东西,以便能够 运行 相同的代码与不同的过滤器并获得相同的类型结果,例如运行 过滤器 .foo.three,.bar[1] 将导致:

{
  "foo": {
    "three": {
      "hello": "world"
    }
  },
  "bar": [
    4
  ]
}

谢谢!

充其量您可以直接通过在 {..} 下命名键名来构建对象,然后应用进一步的转换以仅获取所需的路径

{foo, bar} | .foo |= {three} | .bar |= [.[1]]

jqplay demo

您可以将查询转换为路径,将输入转换为流,select 匹配查询路径的部分,并将其重建为单个输出:

def extract(f):
  reduce (
    path(f) as $path | tostream
    | select(length > 1 and (.[0] | index($path) == 0))
  ) as $set (
   null;
   setpath($set[0]; $set[1])
);

使用 .foo.baz 的第一个示例:

jq 'def extract(f): …; extract(.foo, .baz)'
{
  "foo": {
    "one": true,
    "two": false,
    "three": {
      "hello": "world"
    },
    "four": true
  },
  "baz": true
}

Demo

虽然与稀疏数组一样,它会用 null 填充缺失的项目,否则索引将不再匹配。使用 .foo.three.bar[1] 的第二个示例:

jq 'def extract(f): …; extract(.foo.three, .bar[1])'
{
  "foo": {
    "three": {
      "hello": "world"
    }
  },
  "bar": [
    null,
    4
  ]
}

Demo

这将给出您想要的 .foo.three,.bar[1] 的结果:

jq 'def extract(f):
    . as $input |
    reduce path(f) as $path (
    null;
    if ($path | last | type) == "string"
    then setpath($path; $input | getpath($path))
    else setpath(($path|.[:-1]);
                 getpath($path|.[:-1]) +
                [$input | getpath($path)]
                )
    end
    );
extract(.foo.three, .bar[1])' data.json