pandas.read_json() 默认不递归解析嵌套结构,导致dict/list被存为object类型;需用pd.json_normalize()扁平化,支持record_path、meta、多级路径及错误处理。

直接读取嵌套 JSON 用 pandas.read_json() 通常会失败——它默认只展开一层,深层字典或列表会被塞进单个 object 类型的单元格里,后续无法直接做列操作。
为什么 read_json() 直接读会卡在嵌套字段?
因为 Pandas 默认把 JSON 中的 dict/list 当作原子值处理,不递归解析。比如 {"user": {"name": "Alice", "tags": ["a", "b"]}} 会被读成一列 user,其值是 Python dict 对象,不是展开的 user.name 和 user.tags 两列。
常见错误现象:AttributeError: 'dict' object has no attribute 'str'(想对 df['user'].str.contains(...) 操作时)、ValueError: Must pass DataFrame or 2-d ndarray(传给 pd.concat() 时混入了 list)。
解决思路:必须先扁平化(flatten)结构,再构造 DataFrame。
用 json_normalize() 展开多层嵌套和列表字段
pandas.json_normalize() 是专为这问题设计的函数,比手写递归或 apply(pd.Series) 更稳、更快,且支持路径式字段引用。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 处理单层嵌套:
pd.json_normalize(data, sep='_')自动把{"a": {"b": 1}}变成列a_b - 展开列表字段(如用户订单):
pd.json_normalize(data, record_path='orders', meta=['user_id', 'name']),其中record_path指向要“拉平”的数组字段,meta是每条记录需携带的父级字段 - 处理多级路径:
record_path=['profile', 'history']或meta=[['company', 'id'], ['company', 'name']] - 遇到缺失字段?加
errors='ignore'避免报错;字段名含点号?设sep='|'防止歧义
遇到混合类型或不规则结构怎么办?
真实 JSON 常有字段缺失、同一字段有时是 dict 有时是 null、甚至嵌套深度不一致——json_normalize() 本身不校验 schema,容易产出 NaN 或类型混乱列。
实操建议:
- 先用
pd.json_normalize(data[:5])小样本试跑,检查列名和 dtypes - 对可能为 None 的嵌套字段,预处理:用
[x.get('detail', {}) for x in data]统一兜底,避免json_normalize()报TypeError: string indices must be integers - 列表字段中元素结构不一致?拆成两步:先用
[item for record in data for item in record.get('items', [])]手动展平,再pd.DataFrame() - 性能敏感场景:避免反复调用
json_normalize();大文件优先用chunksize分批读 + 合并,别一次性 load 全量 JSON 到内存
读取后还要处理列表型字段(如 tags、permissions)?
即使 json_normalize() 展开了主结构,字段值仍可能是 Python list(比如 ["admin", "read"]),不能直接 df['roles'].str.contains('admin')。
常用解法:
- 转字符串再匹配:
df['roles'].apply(lambda x: 'admin' in x if isinstance(x, list) else False) - 爆炸成多行:
df.explode('roles').dropna(subset=['roles']),适合做权限统计 - 转独热编码:
df.join(df['tags'].str.join('|').str.get_dummies('|')),注意get_dummies()对空列表会出错,得先 fillna([])
真正麻烦的是嵌套里的嵌套——比如 user.orders.items[].price。这种必须分步 json_normalize():先展开 orders,再对每个 order 的 items 字段单独 normalize,最后 merge 回去。一步到位的写法看着简洁,实际极易漏掉边缘 case。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










