如何使用jq从JSON中获取子对象,在没有Bash处理的情况下保留结果中的最终键?

问题描述 投票:0回答:3

我正在编写一个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中进行预处理?

json bash jq
3个回答
1
投票

有点进一步简化jq部分,但与JawguyChooser的答案具有相同的一般约束,如何更简单的Bash函数

GetSubject () {
    local newroot=${1##*.}
    jq -r "{$newroot: $1}" "$2"
}

我可能会忽略更复杂的Bash处理的一些细微差别,但这似乎适用于您提供的示例。


1
投票

如果我理解你正在努力做的事情,我似乎不可能在阅读文档时做“纯jq”(并且我自己也是常规的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
投票

如果目标是在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”),让jq进行解析,然后使用getpath

如果可以通过将路径与感兴趣的字段分开来调用GetSubobject,这当然是最简单的,如下所示:

GetSubobject .b.x y filename
© www.soinside.com 2019 - 2024. All rights reserved.