Python 从流中生成 JSON 文档

问题描述 投票:0回答:3

我有一个 REST API(RavenDB 的查询流),它返回大量 JSON 格式的数据。一次性加载到内存并解析的量太大了:

问题在于,它不是“每行一个文档”,这会让事情变得非常简单,而是在名为“结果”的字段中返回一个字符串,其中包含我们的文档,如下所示:

{"Results":[ {"Name":"Hello World"} ]}

我真正想做的是使用 python 的请求库来传输响应,如下所示:

r = requests.get('.../streams/query/Raven/DocumentsByEntityName?query=', stream=True) for chunk in r.iter_content(chunk_size=512, decode_unicode=False): print chunk

但我想生成单独的 JSON 文档,这样就不必解析整个响应。一次生成一个 JSON 文档的最有效方法是什么?

python json ravendb
3个回答
1
投票

json.load()

 有一个可选的 
object_pairs_hook
 参数,您可以使用它。这个想法是捕获每个内部
dict
,从回调函数返回一个空字典(或者可能是
None
),以避免在内存中建立巨大的数据结构。

请记住,这不是性能优化:在我的测试中(使用

import simplejson as json

),我发现虽然可以节省内存,但使用钩子检查每个元素实际上会使解析速度慢几倍。不过,如果你记不清了,那也比什么都没有好。


0
投票
这就是我目前的处理方式。我正在做的是匹配大括号 ({}),以便我可以输出

just内部 JSON 文档,每行一个(请参阅:JSON Lines)。

这让我能够将输出流式传输到文本文件,稍后我可以逐行解码该文本文件,而无需解码内存中的整个项目。

欢迎任何建议或优化!

def yield_stream(url1 = '/streams/query/Raven/DocumentsByEntityName?query=', query1=''): r = requests.get(conf.db + url1 + query1, auth=conf.db_auth, stream=True) i = 0 is_doc = False is_str = False doc1 = [] for chunk in r.iter_content(chunk_size=1024, decode_unicode=True): for char in chunk: if is_doc: doc1.append(char) if doc1[-2:-1] != ['\\'] and doc1[-1:] == ['"']: is_str = not is_str if char == '{' and not is_str: i += 1 if i == 2: doc1.append(char) is_doc = True if char == '}' and not is_str: i -= 1 if i == 1: yield ''.join(doc1) doc1 = [] is_doc = False
    

0
投票
我在使 @Aaron 的代码正常工作时遇到问题,并且问题和解决方案略有不同。对我来说,我有 BZ2 压缩的 json,其中记录是背靠背的 {}{}{}{}

我能够以一种内存友好的格式在 Python 3 中运行,每个块仅读取 25 MB 的数据,并提取它找到的所有文档。谢谢@Aaron 提供的最佳解决方案。

def yield_json(file): i = 1 is_doc = False is_str = False doc1 = [] with bz2.open(file, 'r') as bzfh: for chunk in iter(lambda: bzfh.read(READ_BUFFER).decode('utf8'), b''): for char in chunk: if is_doc: doc1.append(char) if doc1[-2:-1] != ['\\'] and doc1[-1:] == ['"']: is_str = not is_str if char == '{' and not is_str: i += 1 if i == 2: doc1.append(char) is_doc = True if char == '}' and not is_str: i -= 1 if i == 1: yield json.loads(''.join(doc1)) doc1 = [] is_doc = Falseenter code here
    
© www.soinside.com 2019 - 2024. All rights reserved.