【问题标题】:How to incrementally write into a json file如何增量写入json文件
【发布时间】:2016-07-09 12:45:41
【问题描述】:

我正在编写一个程序,这需要我生成一个非常大的json 文件。我知道传统的方法是使用json.dump() 转储一个字典列表,但是列表太大以至于在转储之前即使总内存+交换空间也无法容纳它。有没有办法将其流式传输到json 文件中,即将数据逐步写入json 文件中?

【问题讨论】:

  • 您可以在文件开头插入[,然后将您想要的每个值的转储写入数组的新元素。当您关闭文件时,使用 ] 终止它。

标签: python json dictionary


【解决方案1】:

你也可以假设你有一个可迭代的it,你想将它写到一个文件句柄fh作为一个大的JSON记录数组,然后执行以下操作,我认为这是最直接的方法:

def write_json_iter(it, fh):
    print("[", file=fh)
    for n, rec in enumerate(it):
        if n > 0:
            print(",", file=fh)
        json.dump(rec, fh)
    print("]", file=fh)

【讨论】:

    【解决方案2】:

    我知道这已经晚了一年,但问题仍然存在,我很惊讶json.iterencode() 没有被提及。

    本例中iterencode 的潜在问题是,您可能希望使用生成器对大型数据集进行迭代处理,而 json 编码不会序列化生成器。

    解决此问题的方法是使用子类列表类型并覆盖 __iter__ 魔术方法,以便您可以产生生成器的输出。

    这里是这个列表子类的一个例子。

    class StreamArray(list):
        """
        Converts a generator into a list object that can be json serialisable
        while still retaining the iterative nature of a generator.
    
        IE. It converts it to a list without having to exhaust the generator
        and keep it's contents in memory.
        """
        def __init__(self, generator):
            self.generator = generator
            self._len = 1
    
        def __iter__(self):
            self._len = 0
            for item in self.generator:
                yield item
                self._len += 1
    
        def __len__(self):
            """
            Json parser looks for a this method to confirm whether or not it can
            be parsed
            """
            return self._len
    

    从这里开始使用非常简单。获取生成器句柄,将其传递给StreamArray 类,将流数组对象传递给iterencode() 并遍历块。块将是 json 格式的输出,可以直接写入文件。

    示例用法:

    #Function that will iteratively generate a large set of data.
    def large_list_generator_func():
        for i in xrange(5):
            chunk = {'hello_world': i}
            print 'Yielding chunk: ', chunk
            yield chunk
    
    #Write the contents to file:
    with open('/tmp/streamed_write.json', 'w') as outfile:
        large_generator_handle = large_list_generator_func()
        stream_array = StreamArray(large_generator_handle)
        for chunk in json.JSONEncoder().iterencode(stream_array):
            print 'Writing chunk: ', chunk
            outfile.write(chunk)
    

    显示 yield 和 writes 的输出是连续发生的。

    Yielding chunk:  {'hello_world': 0}
    Writing chunk:  [
    Writing chunk:  {
    Writing chunk:  "hello_world"
    Writing chunk:  : 
    Writing chunk:  0
    Writing chunk:  }
    Yielding chunk:  {'hello_world': 1}
    Writing chunk:  , 
    Writing chunk:  {
    Writing chunk:  "hello_world"
    Writing chunk:  : 
    Writing chunk:  1
    Writing chunk:  }
    

    【讨论】:

    • 不错!我只是在研究 json.dump() 默认使用的 iterencode。所以基本上这个答案是:编写你自己的转储函数。这显然不是太难。 ?
    【解决方案3】:

    遗憾的是,json 库没有任何增量编写工具,因此无法满足您的需求。

    这显然会是一个 非常 大的文件 - 其他表示是否更合适?

    否则,我能提出的最佳建议是将每个列表条目转储到内存结构中,并使用必要的分隔符将它们写出来(开头为[,条目之间为],[,结尾为] ) 来尝试构建您需要的 JSON。

    如果格式化很重要,您应该知道程序编写的包装器测试会破坏正确的缩进,但缩进仅适用于人类,因此它不应该对 JSON 结构的语义产生任何影响。

    【讨论】:

    • 格式化并不重要。 json 文件用于将大量文档索引到 Apache Solr 中。我一定会尝试你的方法,然后检查它是否有效。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 2017-09-24
    • 2019-07-03
    • 1970-01-01
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多