我使用ijson来解析大型JSON。我有这段代码,它应该给出对应于相关JSON字段的值的dict:
def parse_kvitems(kv_gen, key_list):
results = {}
for key in key_list:
results[key] = (v for k, v in kv_gen if k == key)
return results
with zipfile.ZipFile(fr'{directory}\{file}', 'r') as zipObj:
# Get a list of all archived file names from the zip
listOfFileNames = zipObj.namelist()
# Iterate over the file names
for fileName in listOfFileNames:
# Check filename endswith csv. dont extract, ijson wants bytes input and json.loads can run into memory issues with smash jsons.No documentation available
if fileName.endswith('.json'):
# Extract a single file from zip
with zipObj.open(fileName) as f:
#HERE:
records = ijson.kvitems(f, 'records.item')
data_list = ['id', 'features', 'modules', 'dbxrefs', 'description']
parsed_records = parse_kvitems(records, data_list) --> give me a dict of dict values that fall under the json headings in data_list我认为keys对象的作用就像一个生成器,并且只通过一次运行(我得到了'id'的期望值,但是parsed_records中的其他data_list键是空的)。
为了解决这个问题,我试着列出一个重复的kv_gen的列表:
def parse_kvitems(kv_gen, key_list):
kv_list = [kv_gen] * len(key_list) #this bit
results = {}
for key, kv_gen in zip(key_list, kv_list):
results[key] = (v for k, v in kv_gen if k == key)
return results这也给了我同样的错误。我认为易变可能是这里的罪魁祸首,但我不能在kvitems对象上使用kvitems来查看它是否修复了。
然后我尝试使用itertools.cycle(),但这似乎是以一种我不明白的方式起作用的:
def parse_kvitems(kv_gen, key_list):
infinite_kvitems = itertools.cycle(kv_gen)
results = {}
for key in key_list:
results[key] = (v for k, v in infinite_kvitems if k == key)
return results另外,下面的内容也很有效(从这个意义上讲,它提供了与我使用json.load()加载JSON时所看到的值相匹配的值):
records = ijson.kvitems(f, 'records.item')
ids = (v for k, v in records if k == 'id')
features = (v for k, v in records if k == 'features')
modules = (v for k, v in records if k == 'modules')我只想知道为什么我的函数没有,特别是当记录对象在上面多次运行时.
编辑罗德里戈
,但是,您并没有显示您如何发现最终的字典中有id的值,而不是其他键的值。我假设这仅仅是因为您首先要迭代解析_records‘’id‘值下的值。当您这样做时,生成器表达式就会被计算出来,底层的kvitems生成器就会耗尽。
是的,这是正确的--我正在将每个val转换成一个列表,以检查每个键都有一个包含相同数量的项目的生成器,因为我担心如果下游zip操作有比最小生成器更多的对象,它们可能会截断一些值。
我没有转换成函数中的列表,因为我认为生成器将是一个更好的返回对象(较少的内存密集型等),然后我可以将其转换为函数外部所需的列表。
,您说您的最后一段代码按照预期工作。这是唯一让我感到惊讶的地方,特别是当您在创建生成器表达式之后真正、真正地检查(即评估)所有三个生成器表达式时。如果您能够澄清是否是这种情况,这将是有趣的;否则,如果您创建了所有三个生成器表达式,但随后评估一个或另一个,那么这里没有意外(因为“关于结果集合”的解释)。
基本上,它给了我希望的值,当我以一个压缩的生成器集合的形式运行这些项时,并将这些项附加到列表中。但是这可能需要更多的调查,JSON非常复杂,所以我可能遗漏了一些东西。
发布于 2021-10-27 13:02:30
关于结果收集
注意如何从kvitems中收集结果。在上面的所有例子中,您使用的是生成器表达式,它们本身就是惰性评估的,这可能会导致误解。然而,您并没有显示您如何发现最终的字典中有id的值,而没有其他键的值。我假设这只是因为您首先要迭代parse_records['id']值下的值。当您这样做时,生成器表达式就会被计算出来,底层的kvitems生成器就会耗尽。当您迭代其他生成器表达式的值时,提供给它们的底层kvitems生成器就会耗尽,因此不会产生任何结果。但是,如果要首先遍历另一个键的值,则应该看到该键的值,而不是其他键的值。
生成器表达式本身是很棒的,但在这种情况下,它可能会增加混乱。如果您想避免这种情况,您可能希望将这些序列合并为列表(例如,使用[... for k, v in kvitems ...]而不是(... for k, v in kvitems ...))。
关于kvitems
正如您所指出的,kvitems是一个单程生成器(或者在输入类似异步文件的对象时是一个单程异步生成器),因此,一旦您对它进行了充分的迭代,进一步的迭代就不会产生任何值。这就是为什么在您的原始代码中确实获得了id的值,而不是在对已经迭代的kvitems对象的后续迭代中收集的其他键的值。
试图复制kvitems对象也是假的:正如您还发现的那样,您只是在所有位置创建一个具有相同对象的列表,而不是原始对象的副本。
尝试copy kvitems是完全不可能的。获得N个“副本”的唯一选项是实际构造N个不同的对象;然而,这意味着输入文件将被读取N次(并且需要打开N次,因为kvitems将推进给定的文件,直到它没有任何更多的输入)。有可能,但不是很好。
itertools.cycle的结果是一个无限生成器。然后使用它作为基础来构造不同的生成器表达式(所以,懒散计算)。你提到这个解决方案是以“你不明白”的方式起作用的,但不要钻研到底发生了什么。我的期望是,当试图检查任何键的值时,您会遇到一个无限循环,因为您的生成器表达式正在无限生成器上迭代,或者类似的东西。
您说您的最后一段代码按照预期工作。这是唯一让我感到惊讶的地方,特别是当您在创建生成器表达式之后真正、真正地检查(即评估)所有三个生成器表达式时。如果您能够澄清是否是这种情况,这将是有趣的;否则,如果您创建了所有三个生成器表达式,但随后评估一个或另一个,那么这里没有意外(因为“关于结果集合”的解释)。
如何解决你的问题
基本上,所有这些都归结为在kvitems上执行一次迭代。例如,您可以尝试这样的方法:
def parse_kvitems(kvitems, keys):
results = collections.defaultdict(list)
for k, v in kvitems:
if k in keys:
results[k].append(v)
return results我想应该可以的。
https://stackoverflow.com/questions/69738173
复制相似问题