如何在嵌套的JSON上使用jq递归,其中每个对象都有一个name属性?

时间:2017-05-12 20:36:08

标签: json filtering jq recursive-datastructures

我有一个嵌套的JSON对象,其中每个级别具有相同的属性键,并且每个级别的区别是名为name的属性。如果我想要遍历到具有特定"路径的水平"在name属性中,我将如何制定jq过滤器?

以下是一些表示文件系统目录结构的示例JSON数据:

{
  "subs": [
    {
      "name": "aaa",
      "subs": [
        {
          "name": "bbb",
          "subs": [
            {
              "name": "ccc",
              "subs": [
                {
                  "name": "ddd",
                  "payload": "xyz"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

用于获取"路径中的有效负载值的jq过滤器" AAA / BBB / CCC / DDD

之前的研究:

  1. jq - select objects with given key name - 很有帮助,但在JSON中查找包含指定名称的任何元素,而我正在寻找一个嵌套在一组对象下的元素,这些元素也包含具体名称。

  2. http://arjanvandergaag.nl/blog/wrestling-json-with-jq.html - 在第4节中有用,它显示了如何提取具有特定值的属性name的对象。但是,执行的递归基于一组特定的已知属性名称(" values []。links.clone []")。在我的情况下,我的等价物只是" subs []。subs []。subs []"。

2 个答案:

答案 0 :(得分:2)

以下jq命令中的过滤器会向下搜索"路径"具有name属性的对象的对应于"路径" AAA / BBB / CCC / DDD:

jq '.subs[] | select(.name = "aaa") | .subs[] | select(.name = "bbb") | .subs[] | select(.name = "ccc") | .subs[] | .payload'

这是qplay.org上的直播:

https://jqplay.org/s/tblW7UX0Si

答案 1 :(得分:2)

以下是通用解决方案的基础:

def descend(name): .subs[] | select(.name == name);

因此,您的特定查询可以表述如下:

descend( "aaa") | descend( "bbb") | descend( "ccc") | descend( "ddd") | .payload

或略好一点,仍使用descend的上述定义:

def path(array): 
  if (array|length)==0 then . 
  else descend(array[0]) | path(array[1:])
  end;

path( ["aaa", "bbb", "ccc", "ddd"] ) | .payload

TCO

path/1的上述递归定义很简单,但不适用于非常深层嵌套的数据结构,例如:如果深度大于1000.这是一个利用jq尾部调用优化的替代定义,因此运行速度非常快:

def atpath(array):
  [array, .] 
  |  until( .[0] == []; .[0] as $a | .[1] | descend($a[0]) | [$a[1:], . ] )
  | .[1];

.aaa.bbb.ccc.ddd

如果您希望能够使用.aaa.bbb.ccc.ddd符号,一种方法是首先“展平”数据:

def flat:
  { (.name): (if .subs then (.subs[] | flat) else .payload end) };

由于顶级元素没有“name”标记,因此查询将为:

.subs[] | flat | .aaa.bbb.ccc.ddd

这是一种更有效的方法,再次使用上面定义的descend

def payload(p):
  def get($array):
    if $array == []
    then .payload
    else descend($array[0]) | get($array[1:]) end;
  get( null | path(p) );

payload( .aaa.bbb.ccc.ddd )