将巨大的JSON文件解析为小文件

问题描述 投票:1回答:4

我具有以下格式的JSON解压缩后,大约有96gzipJSON,这是公开的350 GB JSON文件

{
  "structe": {},
  "beta": {},
  "flow": {
    "1023": {
      "0101": {
        "-LEjllNyHqdHYGntO6vu": {
          "status": "1",
          "t": 1528736191996
        },
        "-LEjllcXKaVOQu3BDpHF": {
          "status": "1",
          "t": 1528736192996
        }
      },
      "0102": {
        "-LEjllNyHqdHYGntO6vu": {
          "status": "1",
          "t": 1528736191996
        },
        "-LEjllcXKaVOQu3BDpHF": {
          "status": "1",
          "t": 1528736192996
        }
      }
    },
    "1024": {
      "0103": {
        "-LEjllNyHqdHYGntO6vu": {
          "lat": 51.128676733981,
          "lng": -113.9318991267252,
          "status": "1",
          "t": 1528736191996
        },
        "-LEjllcXKaVOQu3BDpHF": {
          "lat": 51.128676733981,
          "lng": -113.9318991267252,
          "status": "1",
          "t": 1528736192996
        }
      }
    }
  }
}

我无法将其加载到RAM中,现在我想流传输此文件并将路径flow->1023(let id1)->0101(let id2)拉到新的id1_id2.json文件中。任何人都想如何快速地做到这一点。我正在寻找的输出就像文件名= 1023_0101.json

{
        "-LEjllNyHqdHYGntO6vu": {
          "status": "1",
          "t": 1528736191996
        },
        "-LEjllcXKaVOQu3BDpHF": {
          "status": "1",
          "t": 1528736192996
        }
      }
python json bash stream jq
4个回答
0
投票

我首先想到的是将文件视为流,并逐行读取。已经有一些库将json文件视为流。例如,您可以从ijson库中检出代码段:

对于JSON,如:

{
  "earth": {
    "europe": [
      {"name": "Paris", "type": "city", "info": { ... }},
      {"name": "Thames", "type": "river", "info": { ... }},
      // ...
    ],
    "america": [
      {"name": "Texas", "type": "state", "info": { ... }},
      // ...
    ]
  }
}

治疗看起来像:

import ijson

parser = ijson.parse(urlopen('http://.../'))
stream.write('<geo>')
for prefix, event, value in parser:
    if (prefix, event) == ('earth', 'map_key'):
        stream.write('<%s>' % value)
        continent = value
    elif prefix.endswith('.name'):
        stream.write('<object name="%s"/>' % value)
    elif (prefix, event) == ('earth.%s' % continent, 'end_map'):
        stream.write('</%s>' % continent)
stream.write('</geo>')

0
投票

您可以将jq--stream选项一起使用,并设置为jq - I/O (Streaming),以流方式读取文本,从而允许程序立即开始处理大型JSON文本,而不是在解析完成之后(将整个文件存储在RAM中) 。

假设您输入的ID字符串存储在shell变量上下文中

id1=1023; id2=0101

将巨大的gzip的输出传递到以下过滤器中

jq --arg v1 "$id1" --arg v2 "$id2" --stream 'fromstream(inputs)| objects | .flow[$v1][$v2]' > "$id1"_"$id2".json

((或)如果无法预取id名称,并且您需要在运行时获取它们,则直接将其名称用作

jq --stream 'fromstream(inputs)| objects | .flow."1023"."0101"'

0
投票

这里是使用GNU awk及其gawk-json扩展名的一个:

$ cat program.awk
@load "json"                                                       # load extension
{
    lines=lines $0                                                 # gather json lines
    if(json_fromJSON(lines,datain)==1) {                           # once complete object
        for(i in datain["flow"]["1023"]["0101"])                   # get wanted pieces
            for(j in datain["flow"]["1023"]["0101"][i]) 
                dataout[i][j]=datain["flow"]["1023"]["0101"][i][j] # into another array
        print json_toJSON(dataout)                                 # which gets output
        lines=""                                                   # rinse for next repeat
        delete(dataout)
    }
}

运行它,将每个gz循环到stdout中,以供GNU awk处理:

$ for gz in *.gz ; do gunzip -c $gz ; done | gawk -f program.awk

输出几个gzs的数据:

{"-LEjllcXKaVOQu3BDpHF":{"status":"1","t":1528736192996},"-LEjllNyHqdHYGntO6vu":{"status":"1","t":1528736191996}}
{"-LEjllcXKaVOQu3BDpHF":{"status":"1","t":1528736192996},"-LEjllNyHqdHYGntO6vu":{"status":"1","t":1528736191996}}

对于sissy漂亮打印,输出到jq '.'以上


0
投票

这是一种使用jq的流解析器生成由$ id1,$ id2和相应的感兴趣值组成的流的解决方案;然后可以将该流传输到另一个工具中(例如,如果方便的话,请使用awk)以生成所需的文件。

以下,我们使用jq食谱中的atomize

  def atomize(s):
    fromstream(foreach s as $in ( {previous:null, emit: null};
      if ($in | length == 2) and ($in|.[0][0]) != .previous and .previous != null
      then {emit: [[.previous]], previous: $in|.[0][0]}
      else { previous: ($in|.[0][0]), emit: null}
      end;
      (.emit // empty), $in) ) ;

然后简单地使用(用--stream -n -c调用的主要jq程序:

atomize(inputs)
| select(type == "object" and .flow)
| .flow
| keys_unsorted[] as $id1
| (.[$id1] | keys_unsorted[]) as $id2
| $id1, $id2, .[$id1][$id2]

因此,对于每个gzip文件$ gz,管道如下所示:

gunzip -c $ gz |jq -nc --stream -f program.jq |awk ....

有关使用awk产生所需结果的示例,请参见jq, split a huge json of array and save into file named with a value

注意事项和附录

jq的流解析器避免以牺牲速度为代价使用RAM,因此通常仅在没有其他选择时才使用--stream选项。从问题的描述来看,您似乎可以使用jq的常规解析器处理某些压缩文件,因此您可能希望快速处理这些文件,而对那些太大的文件保留“ atomize”方法。

© www.soinside.com 2019 - 2024. All rights reserved.