【问题标题】:How do I use the 'json' module to read in one JSON object at a time?如何使用“json”模块一次读取一个 JSON 对象?
【发布时间】:2014-03-09 14:43:07
【问题描述】:

我有一个数 GB 的 JSON 文件。该文件由每个不超过几千个字符的 JSON 对象组成,但记录之间没有换行符。

使用 Python 3 和 json 模块,如何一次将一个 JSON 对象从文件读取到内存中?

数据在纯文本文件中。这是一个类似记录的例子。实际记录包含许多嵌套的字典和列表。

以可读格式记录:

{
    "results": {
      "__metadata": {
        "type": "DataServiceProviderDemo.Address"
      },
      "Street": "NE 228th",
      "City": "Sammamish",
      "State": "WA",
      "ZipCode": "98074",
      "Country": "USA"
    }
  }
}

实际格式。新记录一个接一个地开始,没有任何中断。

{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }{"results": { "__metadata": {"type": "DataServiceProviderDemo.Address"},"Street": "NE 228th","City": "Sammamish","State": "WA","ZipCode": "98074","Country": "USA" } } }

【问题讨论】:

  • 发布数据样本,至少有几个对象
  • 你的意思是 JSON 文件是一个对象数组,你想懒惰地读取这些对象?
  • 您是否已经在 Stack Overflow 上搜索过有关此主题的其他帖子?我可以在此处的“相关”侧边栏中至少列出一个。这些帖子如何没有解决您的具体情况?
  • @poke 我不确定你所说的“懒惰”是什么意思,但是是的,我认为这就是我想要的。
  • @MartijnPieters 我能找到的其他帖子都没有解决同样的问题。你能分享你找到的解决方案的链接吗?

标签: python json


【解决方案1】:

一般来说,将多个 JSON 对象放入一个文件会使该文件无效、损坏的 JSON。也就是说,您仍然可以使用 JSONDecoder.raw_decode() method 分块解析数据。

当解析器找到它们时,以下将产生完整的对象:

from json import JSONDecoder
from functools import partial


def json_parse(fileobj, decoder=JSONDecoder(), buffersize=2048):
    buffer = ''
    for chunk in iter(partial(fileobj.read, buffersize), ''):
         buffer += chunk
         while buffer:
             try:
                 result, index = decoder.raw_decode(buffer)
                 yield result
                 buffer = buffer[index:].lstrip()
             except ValueError:
                 # Not enough data to decode, read more
                 break

此函数将从buffersize 块中的给定文件对象中读取块,并让decoder 对象从缓冲区解析整个 JSON 对象。每个已解析的对象都将生成给调用者。

像这样使用它:

with open('yourfilename', 'r') as infh:
    for data in json_parse(infh):
        # process object

仅当您的 JSON 对象被背靠背写入文件时才使用此选项,中间没有换行符。如果您确实有换行符,并且每个 JSON 对象仅限于一行,则您有一个 JSON Lines document,在这种情况下您可以使用 Loading and parsing a JSON file with multiple JSON objects in Python

【讨论】:

  • 效果很好,谢谢。是的,我正在处理的文件有背靠背的 JSON 对象。同样对于 try/except,我使用了“pass”而不是“break”。休息是故意的吗?我无法让它与它一起工作。
  • @user3281420:是的,break 是故意的;它打破了while 循环,因此我们继续从文件中读取下一个块。 break 仅在当前缓冲区中没有要解码的 JSON 对象时触发。
  • @user3281420: pass 仅在缓冲区为空时才有效,因为这是while 循环的另一个终止条件。
  • @RonanDejhero:向buffer 添加.strip() 调用非常简单:result, index = decoder.raw_decode(buffer.strip()) 删除空格,buffer.strip(' \n|') 删除一组明确的字符。
  • @user2441441:data 是解码为 Python 对象的 JSON 数据。这取决于您解码的 JSON 对象如何从中获取键值对。
【解决方案2】:

如果您的 JSON 文档包含一个对象列表,并且您想一次读取一个对象,则可以使用迭代 JSON 解析器 ijson 来完成这项工作。它只会在需要解码下一个对象时从文件中读取更多内容。

请注意,您应该将它与 YAJL 库一起使用,否则您可能不会看到任何性能提升。

话虽如此,除非您的文件真的很大,否则将其完全读入内存,然后使用普通的 JSON 模块解析它可能仍然是最佳选择。

【讨论】:

    【解决方案3】:

    这里对Martijn Pieters' solution 稍作修改,它将处理用空格分隔的 JSON 字符串。

    def json_parse(fileobj, decoder=json.JSONDecoder(), buffersize=2048, 
                   delimiters=None):
        remainder = ''
        for chunk in iter(functools.partial(fileobj.read, buffersize), ''):
            remainder += chunk
            while remainder:
                try:
                    stripped = remainder.strip(delimiters)
                    result, index = decoder.raw_decode(stripped)
                    yield result
                    remainder = stripped[index:]
                except ValueError:
                    # Not enough data to decode, read more
                    break
    

    For example, 如果data.txt 包含由空格分隔的 JSON 字符串:

    {"business_id": "1", "Accepts Credit Cards": true, "Price Range": 1, "type": "food"} {"business_id": "2", "Accepts Credit Cards": true, "Price Range": 2, "type": "cloth"} {"business_id": "3", "Accepts Credit Cards": false, "Price Range": 3, "type": "sports"}
    

    然后

    In [47]: list(json_parse(open('data')))
    Out[47]: 
    [{u'Accepts Credit Cards': True,
      u'Price Range': 1,
      u'business_id': u'1',
      u'type': u'food'},
     {u'Accepts Credit Cards': True,
      u'Price Range': 2,
      u'business_id': u'2',
      u'type': u'cloth'},
     {u'Accepts Credit Cards': False,
      u'Price Range': 3,
      u'business_id': u'3',
      u'type': u'sports'}]
    

    【讨论】:

      猜你喜欢
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-24
      相关资源
      最近更新 更多