datetime等类型需用default参数处理,NaN/Infinity需allow_nan=False校验,中文需ensure_ascii=False,自定义类应实现to_dict方法。

datetime 对象直接 json.dumps 会报错
Python 的 json.dumps 默认不支持 datetime、date、bytes 等类型,遇到就抛 TypeError: Object of type datetime is not JSON serializable。
常见场景是数据库查询结果含时间字段,或日志结构里带 datetime.now(),一序列化就崩。
- 最轻量解法:用
default参数传处理函数,比如lambda obj: obj.isoformat() if isinstance(obj, (datetime, date)) else None - 注意
default函数必须覆盖所有非标类型,否则仍报错;返回None会导致字段消失,不是万能兜底 -
datetime带时区(tzinfo)时,isoformat()输出含+08:00,但某些老系统只认 UTC 或无时区格式,得手动转成obj.astimezone(timezone.utc).isoformat()
NaN、Infinity 在 JSON 中非法但 Python 允许 float 表示
JSON 规范明确禁止 NaN、Infinity、-Infinity,但 Python 的 float 可以表示它们,json.dumps 默认会静默转成 null(不报错!),极易埋雷。
典型触发点:NumPy 计算结果、pandas DataFrame 含空值运算、除零后未检查。
立即学习“Python免费学习笔记(深入)”;
- 用
allow_nan=False强制报错:json.dumps(data, allow_nan=False),这样能早暴露问题 - 如果必须保留语义,可预处理:遍历结构,把
math.isnan(x)或math.isinf(x)的值替换成字符串"NaN"或null,再序列化 - 注意
allow_nan是开关,不是转换器——设为False才真正校验,设为True(默认)反而放行非法值
中文字符被 \uXXXX 转义,前端显示乱码
默认情况下 json.dumps 会对非 ASCII 字符做 Unicode 转义,比如 "你好" 变成 "\u4f60\u597d",虽然合法,但可读性差,某些调试工具或旧版浏览器解析异常。
这不是 bug,是默认行为,源于早期对传输安全的保守设计。
- 加参数
ensure_ascii=False即可输出原生中文:json.dumps(data, ensure_ascii=False) - 但要注意 HTTP 响应头必须声明
Content-Type: application/json; charset=utf-8,否则前端可能按 latin-1 解码 - 若数据要写入文件,推荐显式指定编码:
with open("out.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False)
自定义类实例无法直接序列化
定义了 class User: 并创建实例后,直接 json.dumps(user) 会报 TypeError: Object of type User is not JSON serializable,哪怕它只有几个普通属性。
原因在于 json 模块不认识你的类,也不自动调用 __dict__——它只认内置类型和你显式告诉它的规则。
- 简单对象可用
default=lambda o: o.__dict__,但仅限于纯数据类,且不能有循环引用、不可序列化属性(如文件句柄) - 更稳的方式是让类实现
to_dict()方法,再在default中调用:default=lambda o: o.to_dict() if hasattr(o, "to_dict") else None - 别依赖
vars()或__dict__处理带 property、动态属性或私有字段的类,容易漏字段或暴露不该导出的内容
真正难的不是怎么序列化,而是怎么确保反序列化后还能还原语义——比如时间精度丢没丢、浮点误差有没有放大、空值是 null 还是 "null" 字符串。这些边界一旦跨过,debug 成本远高于加几行 default 函数。










