pandas.json_normalize()可高效展平嵌套json,需先json.loads()解析、合理使用record_path和meta参数,注意混合类型处理、大文件流式读取及键名特殊字符清理。

用 pandas.json_normalize() 处理嵌套字典和列表
pandas.json_normalize() 是专为展平 JSON 结构设计的函数,比手动递归或 pd.DataFrame() 更可靠。它能自动识别嵌套的字典和列表,把路径转成列名(如 "address.city"),还能展开多层嵌套数组。
常见错误是直接传入原始 JSON 字符串——必须先用 json.loads() 解析成 Python 字典/列表;另一个坑是忽略 record_path 和 meta 参数,导致数组字段被丢弃或重复。
- 如果 JSON 顶层是对象(
dict),直接传入json_normalize(data) - 如果要展开某个数组字段(如
"orders"),用record_path="orders",再用meta提取外层字段(如meta=["user_id", "name"]) - 对深层嵌套数组(如
"orders.items"),record_path支持路径列表:record_path=["orders", "items"] - 用
sep="_"可避免点号在后续 SQL 或 Spark 中引发解析问题
处理混合类型字段(None、list、dict 混在同列)
JSON 中同一字段可能有时是字符串、有时是 None、有时是字典,json_normalize() 默认会保留原结构,但后续转 DataFrame 时容易报错或生成 object 列,无法直接做数值运算。
解决办法不是硬编码类型转换,而是利用 errors="ignore" + 后续清洗:
- 先用
json_normalize(data, errors="ignore")避免因某条记录字段缺失而中断 - 对疑似混合列(如
"tags"),检查df["tags"].apply(type).unique()确认类型分布 - 统一转字符串再拆分:
df["tags"].astype(str).str.strip("[]").str.split(", ", expand=True) - 若需保留结构,用
pd.json_normalize(df["metadata"].dropna())单独展平该列
性能瓶颈:大 JSON 文件别一次性全读进内存
当 JSON 文件超 100MB,json.loads() 容易 OOM;即使成功加载,json_normalize() 对深层嵌套数组也会指数级膨胀列数。
实际做法是流式处理:
- 用
jsonlines(pip install jsonlines)逐行读取 NDJSON:for line in jsonlines.open("data.jsonl"): - 每行调用
json_normalize(line, record_path=...),append 到 list,最后pd.concat() - 对超大数组字段(如日志事件列表),先用
itertools.islice()截断前 N 条,验证结构再全量跑 - 禁用
max_level参数(不存在),真正控制深度靠提前 flatten 字段或预处理 JSON
注意 json_normalize() 的边界行为
这个函数不是万能的:遇到键名含点号("user.name")或空格的原始 JSON,会展开成意外的层级;遇到同名但结构不同的嵌套字段(如有的 "profile" 是 dict,有的是 null),会静默填充 NaN 而不报错。
最稳妥的做法是先用 print(json.dumps(data[0], indent=2)) 抽样看结构,再写 record_path;对关键字段,加一行 assert "orders" in data[0] 防止字段名拼错。
展平不是目的,能查、能 join、能导出才是终点——列名里带点号或斜杠,后续用 df.columns = df.columns.str.replace("[./\s]+", "_") 统一清理,比硬扛原始命名更省事。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











