症状
错误“Python TypeError — 对象无法 JSON 序列化”指向一个明确的失效模式。下面依次给出症状、定位该原因的检查命令、根本原因、修复方案,以及证明修复有效的验证步骤。
可观察到的症状包括:
- TypeError: Object of type datetime is not JSON serializable
- json.dumps 正常,直到响应含有日期或集合
- Web 框架在序列化模型对象时返回 500
诊断
先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。
步骤 1
python -c "import json, datetime; json.dumps({'t': datetime.datetime.now()})"
错误会点明确切类型,据此可确定需要哪种转换。
步骤 2
json.dumps(obj, default=lambda o: o.isoformat() if hasattr(o,'isoformat') else str(o))
default 处理器可转换未知类型;ISO 格式日期是常见选择。
常见诱因(按出现频率排序):
- 值中含默认编码器不理解的 datetime、set、Decimal、UUID 或 bytes
- 传入的是 ORM 或类实例,而非纯 dict/list
- 自定义类在边界处缺少 to_dict 或 ISO 转换
根因
标准 JSON 编码器仅支持 dict、list、str、int、float、bool 与 None。其他类型在编码时都要抛 TypeError,除非用 default 可调用或自定义编码器转换。
修复
在边界处转换:日期格式化为 ISO 字符串,set/Decimal 转为 list/float,UUID 转 str,ORM 模型转 dict。提供统一的 default 处理器以保证一致。
注意:用 str() 兜底可能泄露意外表示;更好的是按类型显式转换,而非一律 str。
import json
from datetime import date, datetime
from decimal import Decimal
from uuid import UUID
def default_handler(o):
if isinstance(o, (datetime, date)):
return o.isoformat()
if isinstance(o, (set, frozenset)):
return sorted(o)
if isinstance(o, (Decimal, UUID)):
return str(o)
raise TypeError(f'not serializable: {type(o).__name__}')
payload = {'id': UUID(int=0), 'at': datetime.now(), 'tags': {'a', 'b'}}
print(json.dumps(payload, default=default_handler))
验证
重新 dump 并确认 JSON 合法;解析回来并断言日期以 ISO 字符串往返。为每种自定义类型补充测试。
修复后补充一条回归测试,复现原始症状,防止问题再次出现。
这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。