给出一个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的替代方案不感兴趣,因为我可以想象如何使用其他工具以不同的方式解决这个问题,但我真的很想“只是”使用jq。
发布于 2018-02-21 01:12:58
由于实际需求对我来说并不清楚,我将假设给定的实现定义了功能需求,并提出了一个更简短、更时尚的版本:
. 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]的行为一致。
发布于 2018-02-21 01:06:00
这是可行的:
[
foreach (paths | select(.[-1] == "id" and length > 1)[:-1]) as $path ({i:.};
.o = {
type: last($path[] | strings),
node: (.i | getpath($path))
};
.o
)
]诀窍是要知道路径中的任何数字都表示该值是数组的一部分。您必须调整路径以获取父名称。但是使用带有字符串过滤器的last/1会让它变得更简单。
https://stackoverflow.com/questions/48888057
复制相似问题