Python如何序列化键为namedtuple实例的字典为JSON
问题原因
直接调用json.dumps(test_dict)会直接报错,核心原因有两点:
- JSON标准本身只允许字符串作为对象键,传入namedtuple实例作为键不符合规范,标准库json模块会直接抛出类型错误
- 内置JSON编码器没有适配namedtuple类型的序列化逻辑,即使手动转键也容易丢失类型信息,后续反序列化无法还原原结构。
推荐实现方式
通过自定义JSON编码器,同时处理非字符串键转换、namedtuple结构保留两个逻辑,代码可直接运行:
from collections import namedtuple import json # 原逻辑(注意原示例里的全角引号要换成半角,否则会触发语法错误) FB = namedtuple("FB", ("foo", "bar")) fb = FB(123, 456) test_dict = {fb: 999} class CustomEncoder(json.JSONEncoder): def _process_namedtuple(self, obj): # 统一处理namedtuple的序列化,保留类型标识和字段值 return { "__nt_type": type(obj).__name__, "__nt_fields": obj._asdict() } def default(self, obj): # 序列化遇到namedtuple类型时走自定义逻辑 if isinstance(obj, tuple) and hasattr(obj, "_asdict"): return self._process_namedtuple(obj) return super().default(obj) def encode(self, obj): # 递归遍历所有层级,把字典里的namedtuple键转成合法的JSON字符串 def convert_key(o): if isinstance(o, dict): res = {} for k, v in o.items(): # 识别namedtuple类型的键,转成结构化JSON字符串 if isinstance(k, tuple) and hasattr(k, "_asdict"): k = json.dumps(self._process_namedtuple(k), ensure_ascii=False) res[k] = convert_key(v) return res if isinstance(o, list): return [convert_key(i) for i in o] return o return super().encode(convert_key(obj)) # 执行序列化 json_result = json.dumps(test_dict, cls=CustomEncoder, ensure_ascii=False, indent=2) print(json_result)
序列化输出结果如下,完全符合JSON规范,同时保留了原namedtuple的类型和字段信息:
{ "{\"__nt_type\": \"FB\", \"__nt_fields\": {\"foo\": 123, \"bar\": 456}}": 999 }
简化方案(仅临时导出用)
如果不需要反序列化还原结构,只是临时导出数据,可以直接在构造字典时把namedtuple转成字符串当键,不需要自定义编码器:
test_dict = {str(fb): 999} json_str = json.dumps(test_dict)
这种方式的缺点是键的格式是FB(foo=123, bar=456)这种人类可读格式,没有结构化标记,后续要还原成FB实例需要自己写规则解析,稳定性差,不推荐生产环境使用。
内容的提问来源于stack exchange,提问作者ideate
相关产品推荐
相关产品推荐

