限制jq的输出深度

限制jq的输出深度

我想使用 探索任意文档jq。为此,我想限制jq文档的深度,只显示第一个n(例如 3 个)级别。

假设我有以下 JSON 文档:

{
    "a": {
        "b": {
            "c": {
                "d": {
                    "e": "foo"
                }
            }
        }
    },
    "f": {
        "g": {
            "h": {
                "i": {
                    "j": "bar"
                }
            }
        }
    },
    "k": {
        "l": {
            "m": {
                "n": {
                    "o": "baz"
                }
            }
        }
    }
}

我期待结果

{
    "a": {
        "b": {
            "c": {}
        }
    },
    "f": {
        "g": {
            "h": {}
        }
    },
    "k": {
        "l": {
            "m": {}
        }
    }
}

如果我事先知道文档的结构,那么这是一项相当简单的任务,但我常常不知道。这就是为什么我希望能够jq仅显示文档结构的前 n 层,这可能是字典和数组的任意嵌套。

一个更复杂的例子可能是:

[
    { "a": { "b": { "c": { "d": { "e": "foo"}}}}},
    { "f": [ { "g": "foo"}]},
    [ "h", "i", "j" ]
]

我期望结果的地方

[
    { "a": { "b": {}},
    { "f": [{}]},
    [ "h", "i", "j" ]
]

我可以做jq这个吗?

答案1

del将函数与数组/对象值迭代器结合起来.[]?删除第四层嵌套的任何键/值似乎给出了您正在寻找的结果:

$ jq 'del(.[]?[]?[]?[]?)' <<'EOT'
[
    { "a": { "b": { "c": { "d": { "e": "foo"}}}}},
    { "f": [ { "g": "foo"}]},
    [ "h", "i", "j" ]
]
EOT
[
  {
    "a": {
      "b": {}
    }
  },
  {
    "f": [
      {}
    ]
  },
  [
    "h",
    "i",
    "j"
  ]
]

需要迭代器过滤器.[]?的版本来防止在尝试迭代不是数组或对象的项时出现抱怨。.[]jq

老实说,我.[][]在文档中的任何地方都找不到上面显示的形式(基本上:)中对数组/对象迭代器过滤器的任何直接提及。一个不太简洁但记录清晰的版本是:

$ jq 'del(.[]? | .[]? | .[]? | .[]?)' ...

答案2

使用path(..)您可以为给定文档生成所有可能的路径表达式。然后您可以使用例如选择太长的那些select(length > 3)。这些可以用 删除delpaths()

这样做的好处是允许我们编写一个看起来简洁的表达式,该表达式可以轻松地用单个整数进行参数化(可以将其传递给jqwith --argjson),但该path(..)表达式同时不必要地“重”(产生每一个文档中的路径)。

$ jq 'delpaths([path(..) | select(length > 3)])' file
{
  "a": {
    "b": {
      "c": {}
    }
  },
  "f": {
    "g": {
      "h": {}
    }
  },
  "k": {
    "l": {
      "m": {}
    }
  }
}

使用问题末尾的示例文档进行测试:

$ jq . file
[
  {
    "a": {
      "b": {
        "c": {
          "d": {
            "e": "foo"
          }
        }
      }
    }
  },
  {
    "f": [
      {
        "g": "foo"
      }
    ]
  },
  [
    "h",
    "i",
    "j"
  ]
]
$ jq --argjson d 1 'delpaths([path(..) | select(length > $d)])' file
[
  {},
  {},
  []
]
$ jq --argjson d 2 'delpaths([path(..) | select(length > $d)])' file
[
  {
    "a": {}
  },
  {
    "f": []
  },
  [
    "h",
    "i",
    "j"
  ]
]
$ jq --argjson d 3 'delpaths([path(..) | select(length > $d)])' file
[
  {
    "a": {
      "b": {}
    }
  },
  {
    "f": [
      {}
    ]
  },
  [
    "h",
    "i",
    "j"
  ]
]

答案3

此方法可能并不理想,因为它依赖于可能在 jq 版本之间发生变化的空格,但从某种意义上说,它是一种强大的方法,它可以与其他工具一起应用于类似的情况。

jq 完成漂亮的打印后,它将在 json 树下方的元素前面添加空格。因此,如果您只是对具有一定数量前导空格的行执行反向 grep,则可以删除超出一定深度的任何元素。在下面的示例中,我删除了深度等于或大于 4 的元素,与您的示例类似。

grifball@grifball-Computer:~$ cat limit-output.json
{"a":{"b":{"c":{"d":{"e":"foo"}}}},"f":{"g":{"h":{"i":{"j":"bar"}}}},"k":{"l":{"m":{"n":{"o":"baz"}}}}}
grifball@grifball-Computer:~$ cat limit-output.json | jq
{
  "a": {
    "b": {
      "c": {
        "d": {
          "e": "foo"
        }
      }
    }
  },
  "f": {
    "g": {
      "h": {
        "i": {
          "j": "bar"
        }
      }
    }
  },
  "k": {
    "l": {
      "m": {
        "n": {
          "o": "baz"
        }
      }
    }
  }
}
grifball@grifball-Computer:~$ cat limit-output.json | jq | grep -v '^\s\{8\}'
{
  "a": {
    "b": {
      "c": {
      }
    }
  },
  "f": {
    "g": {
      "h": {
      }
    }
  },
  "k": {
    "l": {
      "m": {
      }
    }
  }
}

相关内容