将JSON数组拆分为单独的文件/对象

9
我有来自Cassandra的JSON数据导出,格式如下。
[
  {
    "correlationId": "2232845a8556cd3219e46ab8",
    "leg": 0,
    "tag": "received",
    "offset": 263128,
    "len": 30,
    "prev": {
      "page": {
        "file": 0,
        "page": 0
      },
      "record": 0
    },
    "data": "HEAD /healthcheck HTTP/1.1\r\n\r\n"
  },
  {
    "correlationId": "2232845a8556cd3219e46ab8",
    "leg": 0,
    "tag": "sent",
    "offset": 262971,
    "len": 157,
    "prev": {
      "page": {
        "file": 10330,
        "page": 6
      },
      "record": 1271
    },
    "data": "HTTP/1.1 200 OK\r\nDate: Wed, 14 Feb 2018 12:57:06 GMT\r\nServer: \r\nConnection: close\r\nX-CorrelationID: Id-2232845a8556cd3219e46ab8 0\r\nContent-Type: text/xml\r\n\r\n"
  }]

我想将其拆分为单独的文档:

{ "correlationId": "2232845a8556cd3219e46ab8", "leg": 0, "tag": "received", "offset": 263128, "len": 30, "prev": { "page": { "file": 0, "page": 0 }, "record": 0 }, "data": "HEAD /healthcheck HTTP/1.1\r\n\r\n" }

{ "correlationId": "2232845a8556cd3219e46ab8", "leg": 0, "tag": "sent", "offset": 262971, "len": 157, "prev": { "page": { "file": 10330, "page": 6 }, "record": 1271 }, "data": "HTTP/1.1 200 OK\r\nDate: Wed, 14 Feb 2018 12:57:06 GMT\r\nServer: \r\nConnection: close\r\nX-CorrelationID: Id-2232845a8556cd3219e46ab8 0\r\nContent-Type: text/xml\r\n\r\n" }

我想使用jq,但找不到方法。

请问如何通过文档分隔符进行拆分?

谢谢,Reddy


1
可能是将JSON文件拆分为单独的文件的重复问题。 - John Zwinck
你需要它适用于任意数量的文档,还是只针对两个文档? - John Zwinck
6个回答

9

要将一个包含许多记录的JSON文件分割成所需大小的块,我只需使用以下方法:

jq -c '.[0:1000]' mybig.json

这里提供了类似于Python切片的功能。

请查看此处的文档:https://stedolan.github.io/jq/manual/

数组/字符串切片:.[10:15]

可以使用.[10:15]语法返回数组的子数组或字符串的子串。由.[10:15]返回的数组将长度为5,包含从索引10(包括)到索引15(不包括)的元素。任一索引都可以是负数(在这种情况下,它会从数组末尾向后计数),也可以省略(在这种情况下,它将指向数组的开头或结尾)。


8
使用jq,可以使用过滤器将数组分割为其组件:
.[]

问题变成了对于每个组件该怎么办。如果您想将每个组件定向到单独的文件中,您可以(例如)使用带有-c选项的jq,并将结果过滤到awk中,然后将组件分配给不同的文件。请参见例如将JSON文件对象拆分为多个文件

性能方面的考虑

有人可能会认为调用jq + awk的开销与调用python相比很高,但是与python + json相比,jq和awk都比较轻量级,如这些时间表所示(使用Python 2.7.10):
time (jq -c  .[] input.json | awk '{print > "doc00" NR ".json";}')
user    0m0.005s
sys     0m0.008s

time python split.py
user    0m0.016s
sys     0m0.046s

4

您可以使用Python更高效地完成此操作(因为您可以一次读取整个输入,而不是每个文档一次):

import json

docs = json.load(open('in.json'))

for ii, doc in enumerate(docs):
    with open('doc{}.json'.format(ii), 'w') as out:
        json.dump(doc, out, indent=2)

@JohnZwinck - 如果你的意思是比多次调用jq更有效率,或许你可以这么说,但对于N=2,我的计时表明,在OP提供的特定数据情况下,你的Python解决方案总体上比两次jq解决方案慢了五倍以上。 - peak
2
@peak:像 N=2 这样的极端情况的性能谁都不关心。你可以试一下 N=10000 吗? - John Zwinck

2

如果您有一个包含2个对象的数组:

jq '.[0]' input.json > doc1.json && jq '.[1]' input.json > doc2.json

结果:

$ head -n100 doc[12].json
==> doc1.json <==
{
  "correlationId": "2232845a8556cd3219e46ab8",
  "leg": 0,
  "tag": "received",
  "offset": 263128,
  "len": 30,
  "prev": {
    "page": {
      "file": 0,
      "page": 0
    },
    "record": 0
  },
  "data": "HEAD /healthcheck HTTP/1.1\r\n\r\n"
}

==> doc2.json <==
{
  "correlationId": "2232845a8556cd3219e46ab8",
  "leg": 0,
  "tag": "sent",
  "offset": 262971,
  "len": 157,
  "prev": {
    "page": {
      "file": 10330,
      "page": 6
    },
    "record": 1271
  },
  "data": "HTTP/1.1 200 OK\r\nDate: Wed, 14 Feb 2018 12:57:06 GMT\r\nServer: \r\nConnection: close\r\nX-CorrelationID: Id-2232845a8556cd3219e46ab8 0\r\nContent-Type: text/xml\r\n\r\n"
}

2
你应该使用 jq '. | length' input.json 来获取文档数量,然后循环执行相应次数。 - John Zwinck
@JohnZwinck,在发表这样的言论之前,你应该与 OP 详细讨论那个时刻;如果你认为那个时刻很关键的话。无论如何,我不认为这种行为对于拥有126K贡献者的声誉是有益的。我感到失望。 - RomanPerekhrest

1
一种方法是使用jq的流选项,并将其管道传输到split命令。
jq -cn --stream 'fromstream(1|truncate_stream(inputs))' bigfile.json | split -l $num_of_elements_in_a_file - big_part

文件中每行的数量取决于您输入的num_of_elements_in_a_file值。您可以查看此答案使用jq如何将一个非常大的JSON文件拆分为多个具有特定对象数量的文件?, 该答案提到了如何使用流解析器的讨论https://github.com/stedolan/jq/wiki/FAQ#streaming-json-parser

1

再举一个例子。 jq -c '.[0:10]' large_json.json > outputtosmall.json


你可以使用 ` 字符来格式化你的代码。 - Adam Calvet Bohl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接