ijson流式解析+jsonpath-ng定位修改是处理大型json深度遍历与修改最可行的组合:ijson.items()按需加载子结构,内存恒定;jsonpath-ng在局部dict/list上原地增删改,避免全文本重解析。

ijson 流式解析 + jsonpath-ng 定位修改,是处理大型 JSON 对象深度遍历与修改最可行的组合。一次性 json.load() 加载几百 MB 甚至 GB 级 JSON 到内存,不是慢,而是根本不可行——会直接触发 MemoryError 或拖垮整个进程。
用 ijson.items() 替代 json.load() 实现按需加载
核心思路:不把整个 JSON 树结构塞进内存,只在需要时拉取某一层或某一批数据。
-
ijson.items(f, 'records.item')可以逐个 yield 数组中每个对象,内存占用恒定(与单条记录大小正相关,而非文件总大小) - 若原始 JSON 是顶层为对象、内部有大数组(如
{"meta":{...},"data":[{"id":1,...},...]}),用ijson.items(f, 'data.item')直接跳过meta解析 - 避免用
ijson.parse()手动过滤事件——易错且逻辑分散;优先选items()或kvitems()这类语义明确的接口 - 注意路径语法:
'items'匹配顶层 key 为items的数组,'items.item'表示该数组的每个元素;路径不支持通配符(如*)或嵌套模糊匹配
用 jsonpath-ng 在局部结构上做精准增删改
流式加载后得到的是一个个 Python dict/list 片段,此时再用 jsonpath-ng 修改就非常轻量——它只操作内存中已存在的子结构,不涉及全文本重解析。
- 增:对单条记录
item,用parse('$.tags').find_or_create(item)创建缺失字段,再赋值 - 改:用
parse('$.user.name').update(item, 'Alice')直接覆写,比手动item['user']['name'] = 'Alice'更健壮(自动处理中间层级缺失) - 删:调用
parse('$.temp_field').filter(lambda x: True, item)后清空匹配节点,或更简单——直接item.pop('temp_field', None) - ⚠️ 注意:
jsonpath-ng的find_or_create()和update()会**原地修改**输入对象,无需重新赋值;但不要在循环中反复调用同一 parser 处理不同对象,应每次新建 parser 实例以防状态污染
绕开序列化瓶颈:避免反复 json.dumps() → json.loads()
很多人在“修改后保存”时习惯先 dumps 成字符串,再 loads 回来——这不仅多一次解析,还丢失了原始数据中的类型细节(如 int 变成 float)和字段顺序。
- 如果只是修改并写回文件,直接用
json.dump(item, output_file, separators=(',', ':'))输出,省掉中间字符串环节 - 若需拼接多条记录为新 JSON 文件(如合并多个批次),用生成器 +
json.JSONEncoder.iterencode()流式编码,避免构造完整 list - 对高频修改场景(如日志打标),考虑把关键字段提取为独立变量(如
item_id = item['id']),后续只操作这些变量,完全跳过 JSON 结构遍历
ijson 流式读,一边指望用 jsonpath-ng 去修改文件里第 100 万条记录的某个字段——因为那条记录早已被 GC 回收。所有修改必须发生在当前加载到内存的片段上,并立即写出或缓存。设计时得明确:这是批处理(read → modify → write),还是实时管道(read → modify → forward),二者 API 选型和错误处理策略完全不同。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











