
本文介绍如何使用递归函数高效地将三层嵌套的 json(含父子层级关系)展平为包含 id、name、code、level 和 parent_id 五列的 pandas dataframe,避免手动多层 normalize 的冗余与错误。
本文介绍如何使用递归函数高效地将三层嵌套的 json(含父子层级关系)展平为包含 id、name、code、level 和 parent_id 五列的 pandas dataframe,避免手动多层 normalize 的冗余与错误。
在处理具有树状结构的 API 响应(如分类体系、组织架构或菜单导航)时,原始 JSON 常以嵌套列表形式存在,层级深度不一。本例中,数据为三层深度的嵌套结构:顶层为 level=1,其 subLevelCategories 字段包含 level=2 的子项,每个 level=2 项又可能包含若干 level=3 的子项。关键挑战在于准确传递并保留父级 id 作为 parent_id,这对后续构建关系型分析、可视化层级图谱或导入数据库至关重要。
手动调用多次 pd.json_normalize()(如先 flatten L1,再取 subLevelCategories[0] flatten L2)不仅代码脆弱(依赖固定索引)、难以泛化,还极易丢失 parent_id 关联——因为 json_normalize 默认不保留外层上下文信息。
✅ 推荐方案是采用递归展平(Recursive Flattening):定义一个函数,接收当前节点列表及父 ID(初始为 0 表示根节点),遍历每个节点,提取核心字段,并将当前节点 id 作为下一层递归调用的 parent_id。该方法逻辑清晰、可读性强、天然支持任意深度(不限于三层),且完全避免索引越界或空值异常。
以下是完整实现代码:
import pandas as pd
def flatten_categories(data, parent_id=0):
"""
递归展平嵌套分类数据,保留层级关系。
Parameters:
-----------
data : list of dict
当前层级的分类列表(如 subLevelCategories)
parent_id : int or None
当前层级所有节点的父级 ID(根节点为 0)
Returns:
--------
list of dict
展平后的记录列表,每条含 id, name, code, level, parent_id
"""
flattened = []
for item in data:
# 提取当前层级基础字段
flat_item = {
'id': item['id'],
'name': item['name'],
'code': item.get('code'), # 使用 .get() 更安全
'level': item['level'],
'parent_id': parent_id
}
flattened.append(flat_item)
# 递归处理子层级(若存在且非空)
children = item.get('subLevelCategories')
if isinstance(children, list) and children:
flattened.extend(flatten_categories(children, parent_id=item['id']))
return flattened
# 示例数据(模拟 API 响应)
data = [
{
"id": 3372,
"name": "Archive",
"code": None,
"level": 1,
"subLevelCategories": [
{
"id": 16708,
"name": ".....",
"code": None,
"level": 2,
"subLevelCategories": [
{"id": 16727, "name": ".........", "code": None, "level": 3, "subLevelCategories": None},
{"id": 16726, "name": "........", "code": None, "level": 3, "subLevelCategories": None}
]
},
{
"id": 16701,
"name": ".......",
"code": None,
"level": 2,
"subLevelCategories": [
{"id": 16782, "name": "......", "code": None, "level": 3, "subLevelCategories": None},
{"id": 16785, "name": "......", "code": None, "level": 3, "subLevelCategories": None}
]
}
]
}
]
# 执行展平并转为 DataFrame
flattened_data = flatten_categories(data)
df = pd.DataFrame(flattened_data)
print(df)
输出结果:
id name code level parent_id 0 3372 Archive None 1 0 1 16708 ..... None 2 3372 2 16727 ......... None 3 16708 3 16726 ........ None 3 16708 4 16701 ....... None 2 3372 5 16782 ...... None 3 16701 6 16785 ...... None 3 16701
? 关键注意事项:
- ✅ 使用 item.get('code') 替代 item['code'] 可防止因字段缺失导致 KeyError;
- ✅ 显式检查 isinstance(children, list) and children 确保只对非空列表递归,兼容 null 或 None 的 subLevelCategories;
- ✅ parent_id=0 作为根节点标识符符合常规设计(也可设为 None,按业务需求调整);
- ⚠️ 若 JSON 规模极大(>10 万节点),需考虑栈深度限制,此时可改用迭代 DFS/BFS 实现,但本例三层结构无需优化。
该方法兼顾健壮性、可维护性与扩展性,是处理树形 JSON 到扁平化 DataFrame 的工业级实践标准。











