使用 jq 处理大型 JSON 流

Mar*_*sse 7 json jq

我从中获得了一个非常大的 JSON 流(几 GB)curl并尝试使用jq.

我想解析的相关输出jq被打包在一个表示结果结构的文档中:

{
  "results":[
    {
      "columns": ["n"],

      // get this
      "data": [    
        {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},
        {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}
      //  ... millions of rows      

      ]
    }
  ],
  "errors": []
}
Run Code Online (Sandbox Code Playgroud)

我想rowjq. 这很简单:

curl XYZ | jq -r -c '.results[0].data[0].row[]'
Run Code Online (Sandbox Code Playgroud)

结果:

{"key1": "row1", "key2": "row1"}
{"key1": "row2", "key2": "row2"}
Run Code Online (Sandbox Code Playgroud)

但是,这总是等到curl完成。

我使用--stream了用于处理此问题的选项。我尝试了以下命令,但也在等待从 返回完整对象curl

curl XYZ | jq -n --stream 'fromstream(1|truncate_stream(inputs)) | .[].data[].row[]'
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以“跳”到该data字段并开始row一个一个解析,而无需等待关闭标签?

Jam*_*ney 9

要得到:

\n\n
{"key1": "row1", "key2": "row1"}\n{"key1": "row2", "key2": "row2"}\n
Run Code Online (Sandbox Code Playgroud)\n\n

从:

\n\n
{\n  "results":[\n    {\n      "columns": ["n"],\n      "data": [    \n        {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},\n        {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}\n      ]\n    }\n  ],\n  "errors": []\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

执行以下操作,与 相同jq -c \'.results[].data[].row[]\',但使用流式传输:

\n\n
jq -cn --stream \'fromstream(1|truncate_stream(inputs | select(.[0][0] == "results" and .[0][2] == "data" and .[0][4] == "row") | del(.[0][0:5])))\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

其作用是:

\n\n
    \n
  • 将 JSON 转换为流(使用--stream
  • \n
  • 选择路径.results[].data[].row[](带有select(.[0][0] == "results" and .[0][2] == "data" and .[0][4] == "row"
  • \n
  • 丢弃路径的初始部分,例如"results",0,"data",0,"row"(with del(.[0][0:5]))
  • \n
  • 最后使用jq FAQfromstream(1|truncate_stream(\xe2\x80\xa6))中的模式将生成的 jq 流重新转换为预期的 JSON
  • \n
\n\n

例如:

\n\n
echo \'\n  {\n    "results":[\n      {\n        "columns": ["n"],\n        "data": [    \n          {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},\n          {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}\n        ]\n      }\n    ],\n    "errors": []\n  }\n\' | jq -cn --stream \'\n  fromstream(1|truncate_stream(\n    inputs | select(\n      .[0][0] == "results" and \n      .[0][2] == "data" and \n      .[0][4] == "row"\n    ) | del(.[0][0:5])\n  ))\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

产生所需的输出。

\n


pea*_*eak 5

(1) 您将使用的香草过滤器如下:

jq -r -c '.results[0].data[].row'
Run Code Online (Sandbox Code Playgroud)

(2) 此处使用流式解析器的一种方法是使用它来处理 的输出.results[0].data,但是将两个步骤结合起来可能会比普通方法慢。

(3) 要产生你想要的输出,你可以运行:

jq -nc --stream '
  fromstream(inputs
    | select( [.[0][0,2,4]] == ["results", "data", "row"])
    | del(.[0][0:5]) )'
Run Code Online (Sandbox Code Playgroud)

(4) 或者,您可能希望尝试以下方法:

jq -nc --stream 'inputs
      | select(length==2)
      | select( [.[0][0,2,4]] == ["results", "data", "row"])
      | [ .[0][6], .[1]] '
Run Code Online (Sandbox Code Playgroud)

对于说明性输入,上次调用的输出将是:

["key1","row1"] ["key2","row1"] ["key1","row2"] ["key2","row2"]