我有一个嵌套的JSON对象,其中每个级别具有相同的属性键,并且每个级别的区别是名为name
的属性。如果我想要遍历到具有特定"路径的水平"在name
属性中,我将如何制定jq
过滤器?
以下是一些表示文件系统目录结构的示例JSON数据:
{
"subs": [
{
"name": "aaa",
"subs": [
{
"name": "bbb",
"subs": [
{
"name": "ccc",
"subs": [
{
"name": "ddd",
"payload": "xyz"
}
]
}
]
}
]
}
]
}
用于获取"路径中的有效负载值的jq
过滤器" AAA / BBB / CCC / DDD
之前的研究:
jq - select objects with given key name - 很有帮助,但在JSON中查找包含指定名称的任何元素,而我正在寻找一个嵌套在一组对象下的元素,这些元素也包含具体名称。
http://arjanvandergaag.nl/blog/wrestling-json-with-jq.html - 在第4节中有用,它显示了如何提取具有特定值的属性name
的对象。但是,执行的递归基于一组特定的已知属性名称(" values []。links.clone []")。在我的情况下,我的等价物只是" subs []。subs []。subs []"。
答案 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上的直播:
答案 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
path/1
的上述递归定义很简单,但不适用于非常深层嵌套的数据结构,例如:如果深度大于1000.这是一个利用jq尾部调用优化的替代定义,因此运行速度非常快:
def atpath(array):
[array, .]
| until( .[0] == []; .[0] as $a | .[1] | descend($a[0]) | [$a[1:], . ] )
| .[1];
如果您希望能够使用.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 )