【问题标题】:Process large JSON stream with jq使用 jq 处理大型 JSON 流
【发布时间】:2016-08-30 15:51:27
【问题描述】:

我从curl 获得了一个非常大的 JSON 流(几 GB)并尝试使用 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": []
}

我想用jq 提取row 数据。这很简单:

curl XYZ | jq -r -c '.results[0].data[0].row[]'

结果:

{"key1": "row1", "key2": "row1"}
{"key1": "row2", "key2": "row2"}

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

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

curl XYZ | jq -n --stream 'fromstream(1|truncate_stream(inputs)) | .[].data[].row[]'

有没有办法“跳转”到data 字段并开始一一解析row 而无需等待结束标签?

【问题讨论】:

    标签: json jq


    【解决方案1】:

    (1) 您将使用的原版过滤器如下:

    jq -r -c '.results[0].data[].row'
    

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

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

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

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

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

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

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

    【讨论】:

    • 这不仅仅是关于速度。如果没有流式传输,内存使用会爆炸并且 curl/jq 崩溃。
    • 谢谢,您的更新确实帮了大忙。我并没有真正到达那里,因为所有行都合并在输出中。而且我还没有弄清楚如何收集各个行的所有键/值对。我更新了问题中的数据描述。
    • 我所要做的就是将所有.[0][3] 相等的数组合并为一个对象。
    【解决方案2】:

    获取:

    {"key1": "row1", "key2": "row1"}
    {"key1": "row2", "key2": "row2"}
    

    发件人:

    {
      "results":[
        {
          "columns": ["n"],
          "data": [    
            {"row": [{"key1": "row1", "key2": "row1"}], "meta": [{"key": "value"}]},
            {"row": [{"key1": "row2", "key2": "row2"}], "meta": [{"key": "value"}]}
          ]
        }
      ],
      "errors": []
    }
    

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

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

    这是做什么的:

    • 将 JSON 转换为流(使用 --stream
    • 选择路径.results[].data[].row[](与select(.[0][0] == "results" and .[0][2] == "data" and .[0][4] == "row"
    • 丢弃路径的那些初始部分,例如"results",0,"data",0,"row"(带有del(.[0][0:5])
    • 最后使用来自jq FAQfromstream(1|truncate_stream(…)) 模式将生成的jq 流转换回预期的JSON

    例如:

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

    产生所需的输出。

    【讨论】:

    • 如果我只想得到"columns": ["n"] 怎么办?
    • @arielnmz jq -cn --stream 'fromstream(1|truncate_stream(inputs | select(.[0][0] == "results" and .[0][2] == "columns") | del(.[0][0:2])))'
    猜你喜欢
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-14
    • 2018-10-21
    相关资源
    最近更新 更多