【问题标题】:json / jq : multi-level grouping of sub-elements in an arrayjson / jq : 数组中子元素的多级分组
【发布时间】:2020-02-24 09:17:41
【问题描述】:

我正在编写一个脚本,该脚本需要将传入的 json 解析为逐行数据,并从多个不同级别的 json 中获取信息。我正在使用jq 来解析数据。

传入的 json 是一个“任务”数组。每个任务[即数组的每个元素] 是一个看起来像这样的对象:

{
  "inputData": {
    "transfers": [
      {
        "source": {
          "directory": "/path/to/source",
          "filename": "somefile.mp3"
        },
        "target": {
          "directory": "/path/to/target",
          "filename": "somefile.mp3"
        }
      },
      {
        "source": {
          "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delivery>content description</delivery>",
          "encoding": "UTF-8"
        },
        "target": {
          "directory": "/path/to/target",
          "filename": "somefile.xml"
        }
      }
    ]
  },
  "outputData": {
    "transferDuration": "00:00:37:10",
    "transferLength": 187813298,
  },
  "updateDate": "2020-02-21T14:37:18.329Z",
  "updateUser": "bob"
}

我想阅读所有任务,并为每个任务输出一行,由以下字段组成:

task[n].inputData.transfers[].target.filename, task[n].outputData.transferLength, task[n].updateDate

我的过滤器链可以正确选择适当的字段,甚至可以从task[].inputData.transfers[] 数组的多个条目中选择“正确”的单个条目,但是当我尝试获取多个元素的输出,链在数组上迭代三遍,我得到了

task[0].inputData.transfers[].target.filename
task[1].inputData.transfers[].target.filename
task[2].inputData.transfers[].target.filename
... 
task[n].inputData.transfers[].target.filename

然后是所有元素的outputData.transferLength 字段的结果,
然后是所有元素的updateDate 字段的结果。

这是我的过滤器链:

'(.tasks[].inputData.transfers[] | select(.target.filename | match("[Xx][Mm][Ll]$")).target.filename), .tasks[].outputData.transferLength, .tasks[].updateDate'

我认为必须有一些有效的方法将数组中的每个元素的所有这些多级元素组合在一起;类似于 'with ...' 子句,例如 with tasks[] : blablabla,但不知道该怎么做。谁能帮忙?

【问题讨论】:

    标签: arrays json jq


    【解决方案1】:

    JSON 示例包含一个多余的 ,jq 不会接受。

    您的示例过滤器链似乎在.tasks[] 上运行,即使该示例似乎只是一个任务。因此,不可能将您所拥有的内容改写为正常运行的状态。因此,与其为不准确的问题提供准确的答案,不如在这里修改过滤器链的三个部分中的第一个:

    .inputData.transfers | map(select(.target.filename | match("xml$"; "i")))
    

    this jqplay snippet

    与其写[ .xs[] | select(p) ],不如写.xs | map(select(p))

    【讨论】:

    • 感谢您的快速回复。对不起,英语不是我的母语。我会尝试重申。我的示例有一个数组的 one 元素。我需要来自同一行的 each 数组元素的所有 3 个字段。逗号需要获得每个元素的 3 个单独部分的输出。您的示例确实提供了一些与我的第一个字段相同的信息,但没有选择我需要的部分,我仍然从所有元素中获取第一个字段,然后从所有元素中获取第二个字段,然后是所有元素的第三个字段,而不是第一个元素的 3 个字段,等等
    • @davidfurst:“同一行上每个数组元素的所有 3 个字段”:“在同一行上”是什么意思?我生成的查询结果不是,而是对象列表。通过“一行”,我假设可能是一个字符串。您能否提供一个您期望的输出示例?也许这比您的解释更容易破译。尽量避免示例中的错误。 StackOverflow 对于多次反复迭代来说并不是一个很好的媒介,所以请尝试在第一次简洁地陈述你的问题。 :-) 我仍然不清楚如何嵌入第二和第三个字段。
    • 当然。我的意思是这样的:

      somefile.xml 472647 2020-02-24T13:04:56.815Z

      这里是 n 元素数组的 1 个元素的 1 行。假设传入的 json 中有 15 个元素,那么我会在结果输出中得到 15 行。我现在得到的是 45 行输出——第一场 15 行,第二场 15 行,第三场 15 行。
    • 我希望这个 jqplay 输出 jqplay.org/s/c049G4bs5x 出现在一行而不是 3 行。
    • @Dmitry 非常感谢!这确实将输出放在一条线上,多亏了你,我学会了一种很好的方法。仍然存在问题,当输入数组有多个元素时,结果仍然作为所有元素的字段1输出,然后是所有元素的字段2,然后是所有元素的字段3,而不是比第一个元素的字段 1,2,3,然后是第二个元素的字段 1,2,3 等等。我仍然会继续尝试其他方法与您的方法相结合,将每个元素的结果放在一行中,
    【解决方案2】:

    我终于找到了答案。诀窍是将.tasks[] 传递到一个表达式中,其中括号作为一个组放置在字段元素周围,这显然会将括号内的任何内容按顺序单独应用于数组的每个元素。然后使用@dmitry 示例作为指导,我还将元素放在左右括号内以重新创建我可以选择的数组元素,然后可以将其输出到每行带有| @csv 的一行。所以对我有用的最后一条链是:

    .task[] | ([.inputData.transfers[].target.filename, .outputData.transferLength, .updateDate]) | [(.[0],.[2],.[3])] | @csv'
    

    不幸的是,我无法让 match() 在此调用中工作,也无法让 sub() 工作;这些中的每一个都会导致 jq 在它转储核心之前提供一个无用的错误消息。

    非常感谢回复的人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2023-04-08
      • 2013-09-21
      相关资源
      最近更新 更多