用jq迭代json

bbmckpt7  于 2023-08-08  发布在  其他
关注(0)|答案(2)|浏览(117)

我正在解析一个API,它向我发送一个JSON响应,如下所示:

{
  "newList": {
    "243": {
      "id": "243",
      "name": "test",
      "create": {
        "date": "2017-08-31 13:57:29"
      }
    },
    "244": {
      "id": "244",
      "name": "test",
      "create": {
        "date": "2017-08-31 13:57:29"
      }
    }
 }
}

字符串
我试图得到名称和创建日期,使用bash与jq,所以几乎没有成功。
jq '.newList'确实可以工作,并将我降低了一个级别,但这还不够。
jq '.newList .243'给了我一个编译错误。此外,243是动态的,可以随时改变。我做错什么了?

slwdgvem

slwdgvem1#

假设根节点名称为newList,如您在给出的问题中所示,要获得id和创建日期,您可以使用jq执行字符串插值

api-producing-json | jq --raw-output '.newList[] | "\(.id) \(.create.date)"'

字符串
这样,过滤器就独立于节点中的动态数字(243244)。如果需要带引号的输出,请删除--raw-output标志。
要在循环中进一步处理,可以在bash循环上迭代,

while read -r id name date; do
    echo "Do whatever with ${id} ${name} ${date}"
done< <(api-producing-json | jq --raw-output '.newList[] | "\(.id) \(.name) \(.create.date)"')


如果您担心任何字段中的空格,则更仔细地分隔单词,在过滤器中使用|作为"\(.id)|\(.name)|\(.create.date)"的分隔符,并在while循环中使用read命令,通过设置IFS=|,以便适当地存储变量。
始终使用官方jq - documentation寻求帮助,并使用这个不错的Playground来测试过滤器,jq - online

nzkunb0c

nzkunb0c2#

第一,容易的部分:您需要.newList."243"(带双引号)才能访问"243"详细信息。jq将.243解释为数字而不是字符串,并且对象键都是字符串。
但正如你所说,你事先不知道密钥,所以我们不能使用特定的密钥。没关系,如果我们需要的话,有办法得到它们。在任何情况下,您的原始问题都只要求输入name和create_date,因此我们可以忽略数字键

简单版本

要丢弃键,我们可以使用values过滤器,然后使用map对值应用其他过滤器。所以我们的解决方案看起来就像cat input.json | jq ' .newList | values | map(some_filter) '
some_filter是什么?它是我们想要的任何东西,只要它能接收正确的对象,并输出我们想要的输出.我过去常常尝试用一行程序来编写这些东西,但我发现如果使用def关键字来定义自己的过滤器(就像在其他语言中定义helper-functions一样),那么考虑jq要容易得多
我建议的解决方案:

cat input.json | jq `
      def details_to_string:
          . | "Details: \(id), \(.name), \(.create.date)";

      .newList | values | map(details_to_string)
`

字符串
这是如何工作的?

  • 在开始处理输入之前,我们定义了一个名为“details_to_string”的新过滤器。它接受一个具有“id”字段和“. create.date”字段的对象,并使用字符串插值返回一个字符串。(我不确定您想要什么样的输出格式,但格式化字符串就是一个简单的示例)。请注意“def”行末尾的冒号和实际过滤器定义末尾的分号
  • 实际处理从最后一行开始。这是输入文件中的JSON对象被输入到程序中的地方
  • 首先,该对象经过.newList过滤器,结果是newList对象({ "243": X, "244": Y },其中XY是较小的JSON对象
  • 接下来,该对象通过values过滤器,结果是一个值数组([ X, Y],其中XY是与上述相同的较小JSON对象)
  • 最后,此数组通过map(details_to_string)。Map接受一个数组,并通过details_to_string过滤器来更改数组中的每一项。因此,它接收像[X_details_object, Y_details_object]这样的数组,并输出像[X_string, Y_string]这样的数组

产量是多少?

[
  "Details: 243, test, 2017-08-31 13:57:29",
  "Details: 244, test, 2017-08-31 13:57:29"
]


如果你只需要单个的字符串(没有方括号,没有逗号),那么你可以把数组的末尾分开。(.newList | values | map(details_to_string) | .[])的最大值。如果您还将“jq”更改为“jq-r”,则也将去掉引号
如果只需要列表中的第一个项目,则可以使用.[0]提取该项目:.newList | values | .[0] | details_to_string(请注意,我们现在已经取消了数组,因此不再需要map)。
更有趣的版本
在上面的评论中,您询问了如何捕获和包含键“243”和“244”,并且您提到这些字符串在名为“id”的字段中不可用,就像在原始示例中一样。为此,我认为最好使用一个名为to_entries的过滤器
to_entries通过将对象的键和值都转换为值来使它们可用。对象中的每一个键值对都变成了一个“入口”对象,看起来像{ "key": some_key, "value": some_value}。注意,“key”和“value”实际上是字符串“key”和“value,”所以像{ "243": "alpha", "244": "Beta"}这样的对象变成了像[ {"key": "243", "value": "alpha"}, {"key": "244", "value": "beta"} ]这样的数组。
这使得密钥可用,但也很难跟踪数据在系统中移动时的结构。这就是我发现将东西拉出到函数中确实有帮助的地方

cat input.json | jq '
        def entry_to_list:
            [ .key, .value.name, .value.create.date ];

        def list_to_string:
          "Details:" + .[0] + ", " +  .[1] + ", " + .[2] ;

       .newList | to_entries | map( entry_to_list | list_to_string )
    '


产出:

[
  "Details: 243, test, 2017-08-31 13:57:29",
  "Details: 244, test, 2017-08-31 13:57:29"
]


在此版本中,您将获得一个中间步骤,其中包含类似'[“244”,“test”,“2017-08-31 13:57:29”]的值,这将为结果排序提供一些选项。(数组首先按第一个字段排序,然后按第二个字段排序,依此类推)

  • newList | to_entries | map(entry_to_list) | sort | map(list_to_string)(在转换为字符串之前对条目进行排序)
  • newList | to_entries | map(entry_to_list) | min | list_to_string(在转换为字符串之前选择最低的条目)

相关问题