如何用Python高效展平含多嵌套数组的复杂JSON?
解决嵌套JSON同时展平多数组字段的问题
我在stackoverflow、geeksforgeeks等平台折腾了好几个小时,想把嵌套JSON展平,但用json_normalize()、flatten_json这些工具都没得到想要的结果。
JSON示例
[ { 'id': '7064574404', 'type': 'INDIVIDUAL', 'name': {'first': 'John', 'middle': 'A.', 'last': 'Doe'}, 'addresses': [ {'address': '774 Pony Ct', 'city': 'Aberdeen', 'state': 'IA', 'zip': '77445', 'phone': '8007777777'}, {'address': '776 S Adams St', 'city': 'Gray Mane', 'state': 'CA', 'zip': '22074', 'phone': '8882384677'}, {'address': '745 E Stallion Ave', 'city': 'White Mane', 'state': 'CA', 'zip': '22074', 'phone': '2234846627'}, {'address': '745 E Stallion Ave', 'city': 'White Mane', 'state': 'CA', 'zip': '22074', 'phone': '2234846627'}, {'address': '757 W Saint George Ave', 'city': 'Mustang', 'state': 'CA', 'zip': '22840', 'phone': '2234645662'}, {'address': '757 W Saint George Ave', 'city': 'Mustang', 'state': 'CA', 'zip': '22840', 'phone': '2234645662'} ], 'specialty': ['Internal Medicine'], 'accepting': 'accepting', 'plans': [ {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740007', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740007', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740004', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740004', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740006', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740007', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740008', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740009', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740070', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740077', 'network_tier': 'NETWORK', 'years': [7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740077', 'network_tier': 'NETWORK', 'years': [7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740074', 'network_tier': 'NETWORK', 'years': [7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740070', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740077', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740074', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740075', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740076', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740077', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740079', 'network_tier': 'NETWORK', 'years': [7077, 7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740040', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740047', 'network_tier': 'NETWORK', 'years': [7077]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740040', 'network_tier': 'NETWORK', 'years': [7074]}, {'plan_id_type': 'HIOS-PLAN-ID', 'plan_id': '70774CA0740047', 'network_tier': 'NETWORK', 'years': [7074]} ], 'languages': ['English'], 'gender': 'Male', 'last_updated_on': '2019-07-12' } ]
需求说明
我要把name(first、middle、last)、addresses(address、city、state、zip、phone)、plans(plan_id_type、plan_id、network_tier、years,years无需拆分)都拆成单独列,最终导出成易读的CSV。我能单独用json_normalize处理addresses和plans,但没法同时处理这两个数组,求解决办法。
我尝试过的代码
df2 = pd.json_normalize( data, "addresses", ["id", "type", "specialty", "accepting", ["name", "first"], ["name", "middle"], ["name", "last"]] ) df2 df3 = pd.json_normalize( data, "plans", ["id", "type", "specialty", "accepting", ["name", "first"], ["name", "middle"], ["name", "last"]] ) df3
解决方案
因为addresses和plans都是多值数组,直接同时展平会生成笛卡尔积(每个地址对应每个计划),这是符合业务逻辑的——每个医生的每个地址都需要关联其所有可用计划。具体步骤如下:
步骤1:处理顶层嵌套字段(name)
先把name的子字段提取为单独列,保留所有顶层信息:
import pandas as pd # 假设data是你的JSON数据列表 df_base = pd.json_normalize(data) # 提取name的子字段并合并到主表,删除原name列 df_base = df_base.join(df_base.pop('name').apply(pd.Series))
步骤2:分别展平addresses和plans数组
单独展平两个数组,保留关联的唯一标识id,方便后续合并:
# 展平addresses数组,关联id df_addresses = pd.json_normalize(data, 'addresses', ['id']) # 展平plans数组,关联id df_plans = pd.json_normalize(data, 'plans', ['id'])
步骤3:合并所有表得到最终结果
通过id将三个表关联,生成完整的展平数据,最后导出CSV:
# 合并基础表和地址表 df_combined = pd.merge(df_base, df_addresses, on='id', how='left') # 再合并计划表,得到所有地址与计划的关联数据 df_final = pd.merge(df_combined, df_plans, on='id', how='left') # 删除不需要的原数组列 df_final = df_final.drop(['addresses', 'plans'], axis=1) # 导出为CSV文件 df_final.to_csv('flattened_doctor_data.csv', index=False)
说明
这样处理后,每个地址会和每个计划组合成一行,确保所有关联关系都被保留。如果你的业务不需要这种笛卡尔积,而是希望把地址/计划作为列表保留,可以调整逻辑,但根据需求拆分为单独列的话,这种方式是最直接有效的。
内容的提问来源于stack exchange,提问作者leesh
相关产品推荐
相关产品推荐

