我正在编写一个Bash函数来获取JSON对象的一部分。该函数的API是:
GetSubobject()
{
local Filter="$1" # Filter is of the form .<key>.<key> ... .<key>
local File="$2" # File is the JSON to get the subobject
# Code to get subobject using jq
# ...
}
为了说明子对象的含义,请考虑Bash函数调用:
GetSubobject .b.x.y example.json
文件example.json包含:
{
"a": { "p": 1, "q": 2 },
"b":
{
"x":
{
"y": { "j": true, "k": [1,2,3] },
"z": [4,5,6]
}
}
}
函数调用的结果将发送到stdout:
{
"y": {
"j": true,
"k": [
1,
2,
3
]
}
}
请注意,代码jq -r "$Filter" "$File"
无法提供所需的答案。它会给:
{ "j": true, "k": [1,2,3] }
请注意,我正在寻找的答案需要是我可以在上面的Bash函数API中使用的。因此,答案应该使用上面显示的过滤器和文件变量,而不是特定于上面的示例。
我想出了一个解决方案;但是,它依靠Bash来完成部分工作。我希望解决方案可以是纯jq,而不依赖于Bash处理。
#!/bin/bash
GetSubobject()
{
local Filter="$1"
local File="$2"
# General case: separate:
# .<key1>.<key2> ... .<keyN-1>.<keyN>
# into:
# Prefix=.<key1>.<key2> ... .<keyN-1>
# Suffix=<keyN>
local Suffix="${Filter##*.}"
local Prefix="${Filter%.$Suffix}"
# Edge case: where Filter = .<key>
# Set:
# Prefix=.
# Suffix=<key>
if [[ -z $Prefix ]]; then
Prefix='.'
Suffix="${Filter#.}"
fi
jq -r "$Prefix|to_entries|map(select(.key==\"$Suffix\"))|from_entries" "$File"
}
GetSubobject "$@"
如何使用jq完成上述Bash功能以获得所需的结果,希望以不那么暴力的方式利用jq的功能而无需在Bash中进行预处理?
答案 0 :(得分:1)
如果我理解你正在尝试做的事情,我似乎无法做到这一点&#34;纯粹的jq&#34;已阅读文档(并且自己成为常规jq
用户)。我能在这里帮助最接近的是简化jq
部分本身:
jq -r "$Prefix| { $Suffix }" "$File"
这与您的示例具有相同的行为(在这组有限的案例中):
GetSubobject '.b.x.y' example.json
{
"y": {
"j": true,
"k": [
1,
2,
3
]
}
}
这实际上是元编程的一种情况,您希望以编程方式对jq
程序进行操作。好吧,(对我而言)jq
将其程序作为输入但不允许您更改程序本身是有道理的。 bash似乎是在这里进行元编程的合适选择:将jq
程序转换为另一个程序,然后使用它运行jq
。
答案 1 :(得分:1)
进一步简化jq
部分,但与JawguyChooser的答案具有相同的一般约束,如何更简单的Bash函数
GetSubject () {
local newroot=${1##*.}
jq -r "{$newroot: $1}" "$2"
}
我可能会忽略更复杂的Bash处理的一些细微差别,但这似乎适用于您提供的示例。
答案 2 :(得分:1)
如果目标是在bash
中尽可能少地做,那么可能以下bash函数将填写该帐单:
function GetSubobject {
local Filter="$1" # Filter is of the form .<key>.<key> ... .<key>
local File="$2" # File is the JSON to get the subobject
jq '(null|path('"$Filter"')) as $path
| {($path[-1]): '"$Filter"'}' "$File"
}
另一种方法是将$Filter
作为字符串传递(例如--arg filter&#34; $ Filter&#34;),让jq进行解析,然后使用getpath
。
如果可以使用与感兴趣的字段分开的路径调用GetSubobject,这当然是最简单的,如下所示:
GetSubobject .b.x y filename