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

2024-05-29 04:52:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我有大约96gzip的JSON文件,在用下面的结构解压之后,这个JSON文件超过350gb

{
  "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
        }
      }

Tags: 文件jsonstatusflow结构betaletlng
3条回答

您可以使用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"'

这里有一个解决方案,它使用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) ) ;

主jq程序(用stream-n-c调用)是:

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

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

gunzip-加元| jq-nc流-f程序.jq| 啊。。。。你知道吗

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

警告和附录

jq的流解析器避免以牺牲速度为代价使用RAM,因此通常使用stream选项只是作为最后的手段。从问题的描述来看,您可能可以使用jq的常规解析器处理一些压缩文件,因此您可能希望快速处理这些文件,而将“原子化”方法留给那些太大的文件。你知道吗

小心

问题描述没有明确说明如果发生id1\u id2.json冲突应该怎么做。如果不可能发生这样的碰撞,那当然没有问题。否则,将由创建这些文件的程序来管理意外事件。你知道吗

我首先想到的是把文件当作一条流,一行一行地读。已经有一些库将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>')

相关问题 更多 >

    热门问题