json.dumps() 默认不支持 datetime、Decimal 和自定义类,需通过 default 参数或自定义 JSONEncoder 实现序列化:datetime/date 转 ISO 字符串,Decimal 推荐转 str 保精度,自定义类优先用 to_dict() 方法。

直接用 json.dumps() 处理 datetime、Decimal 或自定义类对象会报错,因为 JSON 标准只支持 str、int、float、bool、None、list、dict 这几种类型。解决的关键是提供一个能识别并转换这些“不兼容类型”的 default 回调函数。
用 default 参数统一转换特殊类型
这是最常用也最灵活的方式。你写一个函数,判断对象类型,返回 JSON 可序列化的等价表示:
-
datetime.date/datetime.datetime→ 转成 ISO 格式字符串(如"2024-05-20"或"2024-05-20T14:30:00.123") -
decimal.Decimal→ 转成float(注意精度损失)或str(推荐,保留精度) - 自定义类 → 返回其
__dict__,或显式定义字段字典(更可控)
示例:
import json from datetime import datetime, date from decimal import Decimaldef json_serializer(obj): if isinstance(obj, (datetime, date)): return obj.isoformat() if isinstance(obj, Decimal): return str(obj) # 避免 float 精度问题 if hasattr(obj, 'dict'): return obj.dict raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
data = { "time": datetime.now(), "price": Decimal("99.99"), "user": type('User', (), {"name": "Alice", "age": 30})() }
json.dumps(data, default=json_serializer, ensure_ascii=False)
为自定义类实现 to_dict() 方法更清晰
比起依赖 __dict__(可能包含私有属性、方法、循环引用),在类里明确定义 to_dict() 更安全、可维护性更高:
class Product:
def __init__(self, name, price, created_at):
self.name = name
self.price = price
self.created_at = created_at
def to_dict(self):
return {
"name": self.name,
"price": str(self.price),
"created_at": self.created_at.isoformat() if self.created_at else None
}在 serializer 中调用
def json_serializer(obj):
if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
return obj.to_dict()
其他类型处理...
用 cls 参数继承 JSONEncoder 更适合项目级复用
如果多个地方都需要相同规则,继承 json.JSONEncoder 并重写 default 方法,代码更结构化,也方便统一配置(比如全局控制日期格式):
class CustomJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(obj, date):
return obj.isoformat()
if isinstance(obj, Decimal):
return float(obj) if obj.as_tuple().exponent >= 0 else str(obj)
if hasattr(obj, 'to_dict'):
return obj.to_dict()
return super().default(obj)
json.dumps(data, cls=CustomJSONEncoder, ensure_ascii=False)
小提醒:避免踩坑
别用 str(obj) 一锅端 —— 对于 datetime 是 OK 的,但对 Decimal('1.00') 得到 "1.00" 还行,而对自定义类可能输出 "<__main__.user object at>",毫无意义。
慎用 float(Decimal) —— 小数位数多时会丢失精度,金融场景务必用 str()。
注意嵌套对象 —— 如果自定义类属性里还有 datetime 或 Decimal,确保 to_dict() 或 default 递归处理它们。










