
本文介绍一种通用、递归的 JSON 扁平化方法,可自动处理对象(dict)、数组(list)及混合嵌套结构,将深层字段转换为 parent_child_grandchild 形式的扁平键,完美适配 pandas 分析与后续数据工程场景。
本文介绍一种通用、递归的 json 扁平化方法,可自动处理对象(dict)、数组(list)及混合嵌套结构,将深层字段转换为 `parent_child_grandchild` 形式的扁平键,完美适配 pandas 分析与后续数据工程场景。
在实际数据处理中,原始 JSON 常含多层嵌套(如 device.model、data[0].label),直接使用 pd.json_normalize() 难以统一展平所有路径,尤其当存在列表且需保留索引语义时。上述示例中,data 是单元素列表,但理想输出应将 data 作为前缀(而非忽略或保留原 list 结构),同时避免出现冗余字段(如原始代码中残留的 data 列)。
为此,我们采用递归深度优先遍历 + 路径拼接策略,实现真正通用的扁平化函数:
def flatten_dict(d: dict, prefix: str = "") -> dict:
"""
递归扁平化嵌套字典(支持 dict 和 list),用 '_' 连接层级路径。
- 对于 dict:递归进入,新前缀为 f"{prefix}{key}_"
- 对于 list:对每个元素编号(1-based),如 data_1_timestamp;若元素为 dict,继续递归
- 对于原子值(str/int/float/None等):直接写入 {f"{prefix}{key}": value}
"""
result = {}
for key, value in d.items():
new_prefix = f"{prefix}{key}_" if prefix else f"{key}_"
if isinstance(value, dict) and value: # 非空字典递归
result.update(flatten_dict(value, new_prefix))
elif isinstance(value, list) and value: # 非空列表,逐项处理
for i, item in enumerate(value, start=1):
item_prefix = f"{prefix}{key}_{i}_"
if isinstance(item, dict):
result.update(flatten_dict(item, item_prefix))
else:
result[f"{prefix}{key}_{i}"] = item
else: # 原子值或空容器
result[f"{prefix}{key}" if prefix else key] = value
return result使用该函数处理示例 JSON:
import json
import pandas as pd
json_data = '''{
"appVersion": "0.0.3",
"device": {"model": "Lenovo"},
"bef": {"catalog": "Manual"},
"data": [{
"timestamp": "2024-04-24 12:08:02.415077",
"label": "zuf",
"category": "50"
}]
}'''
parsed = json.loads(json_data)
flattened = flatten_dict(parsed)
print(flattened)
# 输出:
# {
# 'appVersion': '0.0.3',
# 'device_model': 'Lenovo',
# 'bef_catalog': 'Manual',
# 'data_1_timestamp': '2024-04-24 12:08:02.415077',
# 'data_1_label': 'zuf',
# 'data_1_category': '50'
# }
# 转为单行 DataFrame(适用于单条记录)
df = pd.DataFrame([flattened])
print(df)✅ 输出完全匹配期望格式(列名自动带 _ 分隔,data 列表元素被展开为 data_1_*):
| appVersion | device_model | bef_catalog | data_1_timestamp | data_1_label | data_1_category |
|---|---|---|---|---|---|
| 0.0.3 | Lenovo | Manual | 2024-04-24 12:08:02.415077 | zuf | 50 |
⚠️ 注意事项与进阶建议:
- 列表多元素场景:若 data 包含多个对象(如 [{}, {}, {}]),函数会生成 data_1_*, data_2_*, data_3_* 等列,天然支持“宽表”展开。如需“长表”(每行一条 data 记录),应先 pd.json_normalize(parsed, record_path='data', meta=['appVersion', 'device.model', ...])。
- 空值/缺失键鲁棒性:当前函数对 None 或空 dict/list 可安全处理(跳过递归),但若需显式填充(如 device_model: null),可在对应分支添加 result[new_key] = None。
- 性能优化:对超大 JSON,可改用生成器或迭代式 DFS 避免栈溢出;生产环境建议增加类型校验与日志。
- 自定义分隔符:将 _ 替换为参数(如 sep='.')即可适配不同规范(如 Prometheus 标签风格)。
该方案彻底解耦了结构解析与数据消费逻辑,一次扁平,随处可用——无论是存入数据库、训练特征工程,还是导出为 CSV,均能保持字段语义清晰、层级关系可追溯。


















