
本文介绍如何在 Python 中解析含重复键的 JSON 字符串,将相同键对应的所有值用分号连接为单一字符串,生成标准字典结构。核心方法是利用 json.loads 的 object_pairs_hook 参数配合 itertools.groupby 实现键值聚合。
本文介绍如何在 python 中解析含重复键的 json 字符串,将相同键对应的所有值用分号连接为单一字符串,生成标准字典结构。核心方法是利用 `json.loads` 的 `object_pairs_hook` 参数配合 `itertools.groupby` 实现键值聚合。
在标准 JSON 规范中,对象(object)不允许存在重复键;但实际开发中(尤其在 API 响应、日志数据或遗留系统导出文件中),常会遇到人为构造或解析异常导致的重复键 JSON。Python 默认的 json.loads() 仅保留最后一个同名键的值,直接丢失其余数据。若需保留全部值并按指定分隔符合并,必须绕过默认行为,接管键值对的解析过程。
关键在于使用 json.loads() 的 object_pairs_hook 参数——它允许传入一个函数,接收原始 JSON 解析出的有序键值对列表(list of tuples),而非默认的 dict。这为我们提供了对重复键进行分组与聚合的机会。
以下是一个健壮、可复用的解决方案:
from itertools import groupby
import json
def merge_duplicates(pairs):
"""
将键值对列表按 key 分组,对每个 key 的所有 value 用 ';' 连接
注意:groupby 要求输入已按 key 排序
"""
sorted_pairs = sorted(pairs, key=lambda x: x[0])
for key, group in groupby(sorted_pairs, key=lambda x: x[0]):
values = [value for _, value in group]
yield key, ';'.join(values)
def parse_json_with_merged_keys(json_str):
"""
解析含重复键的 JSON 字符串,自动合并同 key 的 value(以 ';' 分隔)
"""
return json.loads(json_str, object_pairs_hook=lambda pairs: dict(merge_duplicates(pairs)))
# 示例使用
input_json = '''
{
"1061": "GROCERY",
"1073": "GM-HBC",
"4220": "PRODUCE",
"958": "MEAT",
"958": "DAIRY",
"958": "FROZEN"
}
'''
result = parse_json_with_merged_keys(input_json)
print(result)
# 输出: {'1061': 'GROCERY', '1073': 'GM-HBC', '4220': 'PRODUCE', '958': 'DAIRY;FROZEN;MEAT'}✅ 工作原理说明:
立即学习“Python免费学习笔记(深入)”;
- json.loads(..., object_pairs_hook=...) 确保原始键值对顺序被完整传递(不被 dict 自动去重);
- sorted(pairs, key=lambda x: x[0]) 按键升序排序,满足 groupby 的分组前提;
- groupby(..., key=lambda x: x[0]) 将相同键的元组归为一组;
- ';'.join(...) 高效拼接所有对应值,支持任意数量重复键。
⚠️ 注意事项:
- 该方法不修改原始 JSON 字符串,仅影响解析逻辑;
- 若原始 JSON 中 value 本身含分号(;),需提前转义或改用其他分隔符(如 '|' 或 '\n'),并在业务层做好兼容;
- object_pairs_hook 在 Python 3.7+ 中稳定支持,无需额外依赖;
- 对于超大 JSON 文件,建议结合 json.JSONDecoder 流式解析以控制内存占用。
? 进阶提示:
如需支持自定义分隔符或空值过滤,可扩展 merge_duplicates 函数:
def merge_duplicates(pairs, separator=';', skip_none=True):
sorted_pairs = sorted(pairs, key=lambda x: x[0])
for key, group in groupby(sorted_pairs, key=lambda x: x[0]):
values = [v for _, v in group if not (skip_none and v is None)]
yield key, separator.join(values)通过此方案,你能在保持代码简洁的同时,精准解决“重复键值合并”这一典型数据清洗需求,适用于 ETL、API 数据标准化及配置文件预处理等场景。










