JSONEncoder默认不支持datetime,需继承并重写default方法,针对datetime/date/time返回isoformat()结果,确保时区信息和精度不丢失。

JSONEncoder默认不支持datetime怎么办
Python的json.dumps()直接序列化含datetime对象的数据会报TypeError: Object of type datetime is not JSON serializable。这不是bug,是设计使然——JSON标准本身没有日期类型,Python默认不替你做隐式转换。
常见错误是试图用str(dt)或dt.isoformat()手动预处理所有字段,但这样侵入性强、易漏、难维护。真正可控的方式是重写JSONEncoder子类。
继承JSONEncoder并重写default方法
default是JSONEncoder的钩子方法,当遇到无法序列化的对象时被调用。它必须返回一个可序列化的值,或抛出TypeError(继续向上冒泡)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 只在
default里处理你明确知道要支持的类型,比如datetime、date、time,不要无差别return str(obj)——这会把Decimal、自定义类等全转成字符串,丢失语义 - 优先用
isoformat()而非strftime(),前者更标准、能保留时区信息(如果datetime带tzinfo) - 对
date和time也单独判断,避免isinstance(obj, datetime)漏掉纯日期
示例:
import json
from datetime import datetime, date, time
<p>class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, date):
return obj.isoformat()
elif isinstance(obj, time):
return obj.isoformat()
return super().default(obj)</p><p>json.dumps({"now": datetime.now(), "today": date.today()}, cls=DateTimeEncoder)</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill4292" title="jm-jsjkxyjs02-pzl-803"><img
src="https://img.php.cn/upload/skill/000/000/081/178998482628832.jpg" alt="jm-jsjkxyjs02-pzl-803" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill4292" title="jm-jsjkxyjs02-pzl-803">jm-jsjkxyjs02-pzl-803</a>
<p>查询全球任意城市的实时天气和未来天气预报</p>
</div>
<a href="/xiazai/skill4292" title="jm-jsjkxyjs02-pzl-803" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>→ {"now": "2024-06-12T15:23:45.123456", "today": "2024-06-12"}</h1><p>时区敏感场景下要注意什么
如果原始datetime是naive(无时区),isoformat()输出不带Z或偏移;如果是aware(有时区),会自动带上+08:00或Z。前端解析时依赖这个格式,所以不能随意删掉时区信息。
容易踩的坑:
- 用
strftime("%Y-%m-%d %H:%M:%S")丢弃了毫秒和时区,导致精度丢失且无法无损反序列化 - 对aware时间强行用
replace(tzinfo=None)再序列化,等于抹掉时区上下文,后续可能引发时间错乱 - 没统一后端时区策略,有的地方用UTC,有的用本地时区,encoder里混着处理,调试时极难定位
建议:后端内部统一用UTC存储和传递datetime,encoder只做标准isoformat()输出,把时区解释逻辑交给前端或业务层。
要不要在encoder里支持更多类型(如Decimal、UUID)
可以,但得想清楚责任边界。一个encoder承担太多类型转换,会变得难以测试、复用性差。比如Decimal通常应转为float还是str?金融场景必须用字符串保精度,而统计场景用浮点更方便。
更务实的做法:
- 基础encoder只覆盖
datetime/date/time——这是最常踩的坑,且语义明确 - 按需组合:需要
Decimal支持时,另写DecimalEncoder,再用多重继承或封装(如class MyEncoder(DateTimeEncoder, DecimalEncoder)),避免大杂烩 - 警惕
object.__dict__式兜底:别在default里写return obj.__dict__,它会暴露私有属性、循环引用、不可序列化成员,大概率崩溃
真正难的不是写几行default,而是确定哪些类型该由encoder负责、哪些该由数据层提前规整——这个决策比代码本身影响更久。

















