json.dumps() 默认仅支持python内置类型,序列化自定义类会因无法识别而报typeerror;需通过default参数指定转换函数,并注意ensure_ascii、sort_keys及文件编码等细节。

json.dumps() 为什么直接序列化自定义类会报 TypeError: Object of type XXX is not JSON serializable
因为 json.dumps() 默认只认 Python 内置类型(dict、list、str、int、float、bool、None),遇到自定义类实例时根本不知道怎么转成 JSON 字段。它不会自动调用 __dict__,也不会尝试读取属性——这是明确的设计限制,不是 bug。
常见错误现象:你写了 json.dumps(MyClass()),结果抛出上面那个 TypeError,而不是静默忽略或报错不清晰。
- 不要指望
default参数为空时能“猜”出你的意图 - 不要在类里只加个
to_json()方法就以为json.dumps()会自动发现并调用它 - 如果对象含
datetime、bytes、Decimal等非标准类型,同样会触发该错误
用 default 参数把自定义对象转成字典再序列化
最常用也最可控的方式是给 json.dumps() 传一个 default 函数,它会在遇到无法序列化的对象时被调用,返回一个 JSON 可接受的替代值(通常是 dict 或基础类型)。
实操建议:
- 检查对象类型,只处理你关心的类,其他类型原样抛出异常,避免掩盖真正问题:
def json_default(obj): if isinstance(obj, MyUser): return {"name": obj.name, "age": obj.age} raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable") - 别在
default里做深度递归转换(比如遍历所有属性),容易引发循环引用或意外暴露私有字段 - 如果多个类要支持,可以用
isinstance(obj, (MyUser, Order, Product))批量判断,但建议按职责拆分逻辑,避免函数越来越胖
对字典本身,要注意 ensure_ascii=False 和 sort_keys
很多人导出中文时看到一堆 \u4f60\u597d,是因为 json.dumps() 默认开启 ensure_ascii=True,强制把非 ASCII 字符转义。这不是编码问题,是 JSON 规范兼容性策略。
- 输出可读中文:必须显式加
ensure_ascii=False,否则前端或人工查看时极难调试 - 需要稳定键序(比如用于签名、diff 对比):加上
sort_keys=True,否则 Python 3.7+ 虽然保持插入顺序,但不同运行环境或 dict 实现仍可能有微小差异 - 不要依赖默认缩进(
indent=None)生成可读 JSON;调试时加indent=2,上线前关掉以减小体积
别用 json.dump() 直接写文件而不处理编码
json.dump() 写文件时,如果没指定 encoding,且文件对象是以文本模式打开(如 open("out.json", "w")),Python 会用系统默认编码(Windows 上常是 cp936),导致中文写入失败或乱码,错误信息可能是 UnicodeEncodeError: 'charmap' codec can't encode character。
正确做法只有这一种:
- 始终用
open(..., "w", encoding="utf-8")显式声明编码 - 不要依赖
io.TextIOWrapper的隐式行为,尤其在跨平台部署时 - 如果必须用二进制模式(比如拼接其他二进制数据),那就用
json.dumps(...).encode("utf-8"),再写入open(..., "wb")
真正麻烦的从来不是“怎么让代码跑起来”,而是当对象嵌套了 3 层、含 datetime 和自定义枚举、还要兼容旧版 API 返回格式时,default 函数里那一堆 hasattr 和类型判断有没有漏边角 case。这种地方多打一行日志,比反复改 JSON 结构省半天事。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











