给出JSON中的示例文档,类似于:
{
"id": "post-1",
"type": "blog-post",
"tags": [
{
"id": "tag-1",
"name": "Tag 1"
},
{
"id": "tag-2",
"name": "Tag 2"
}
],
"heading": "Post 1",
"body": "this is my first blog post",
"links": [
{
"id": "post-2",
"heading": "Post 2",
"tags": [
{
"id": "tag-1",
"name": "Tag 1"
},
{
"id": "tag-3",
"name": "Tag 3"
}
]
}
],
"metadata": {
"user": {
"social": [
{
"id": "twitter",
"handle": "@user"
},
{
"id": "facebook",
"handle": "123456"
},
{
"id": "youtube",
"handle": "ABC123xyz"
}
]
},
"categories": [
{
"name": "Category 1"
},
{
"name": "Category 2"
}
]
}
}
我想选择具有属性"id"
的任何对象(无论深度),以及父对象的属性名称。上面的例子应该只是一个例子。我不能自由分享的实际数据可以有任何深度和任何结构。可以随时引入和删除属性。使用Blog Post样式只是因为它非常受欢迎,我的想象力非常有限。
该属性表示域中的特定类型,也可能(但不一定)编码为属性的值。
如果一个对象没有"id"
属性,那么它就没有意义,不应该被选中。
一个非常重要的特殊情况是当属性的值是一个对象数组时,在这种情况下,我需要保留属性名称并将其与数组中的每个元素相关联。
所需输出的一个例子是:
[
{
"type": "tags",
"node": {
"id": "tag-1",
"name": "Tag 1"
}
},
{
"type": "tags",
"node": {
"id": "tag-2",
"name": "Tag 2"
}
},
{
"type": "links",
"node": {
"id": "post-2",
"heading": "Post 2",
"tags": [
{
"id": "tag-1",
"name": "Tag 1"
},
{
"id": "tag-3",
"name": "Tag 3"
}
]
}
},
{
"type": "tags",
"node": {
"id": "tag-1",
"name": "Tag 1"
}
},
{
"type": "tags",
"node": {
"id": "tag-3",
"name": "Tag 3"
}
},
{
"type": "social",
"node": {
"id": "twitter",
"handle": "@user"
}
},
{
"type": "social",
"node": {
"id": "facebook",
"handle": "123456"
}
},
{
"type": "social",
"node": {
"id": "youtube",
"handle": "ABC123xyz"
}
}
]
输出是完全相同的,输出是完全相同的,例如,与我的用例无关,它也可以被分组。由于顶级对象具有属性"id"
,因此可以包含特殊名称,但如果根本不包含该名称,我会更喜欢。
我尝试使用walk
,reduce
和recurse
无济于事,我担心我的jq
技能太有限了。但我认为一个好的解决方案至少会使用其中一个。
我想要一个类似
的表达式to_entries[] | .value | .. | select(has("id")?)
会选择正确的对象,但..
我不再能够保留关联的属性名称。
我提出的最好的是
. as $document
| [paths | if length > 1 and .[-1] == "id" then .[0:-1] else empty end]
| map(. as $path
| $document
| { "type": [$path[] | if type == "string" then . else empty end][-1],
"node": getpath($path) })
哪个有效,但感觉非常复杂并且涉及首先提取所有路径,忽略任何没有"id"
作为最后一个元素的路径,然后删除"id"
段以获取实际路径对象并存储(现在是最后一个)作为字符串的段,该段对应于包含感兴趣对象的父对象属性。最后,通过getpath
选择实际对象。
是否有更优雅或最短的表达方式?
我应该注意,我希望使用jq
以方便绑定到其他语言以及能够在命令行上运行程序。
对于这个问题的范围,我并不真正对jq
的替代品感兴趣,因为我可以想象如何使用其他工具以不同的方式解决这个问题,但我真的很想"只是& #34;使用jq
。
答案 0 :(得分:1)
这有效:
[
foreach (paths | select(.[-1] == "id" and length > 1)[:-1]) as $path ({i:.};
.o = {
type: last($path[] | strings),
node: (.i | getpath($path))
};
.o
)
]
诀窍是要知道路径中的任何数字都表示该值是数组的一部分。您必须调整路径才能获取父名称。但是使用带有字符串过滤器的last/1
会使其更简单。
答案 1 :(得分:1)
由于实际要求对我来说并不清楚,我将假设给定的实现定义了功能要求,并提出了一个更短,更有希望的更光滑的版本:
. as $document
| paths
| select(length > 1 and .[-1] == "id")
| .[0:-1] as $path
| { "type": last($path[] | strings),
"node": $document | getpath($path) }
这会产生一个流,所以如果你想要一个数组,你可以简单地将上面的方括号括起来。
如果流为空,则 last(stream)
会发出null,这符合.[-1]
的行为。