
本文介绍如何在 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 数据标准化及配置文件预处理等场景。