json.dumps序列化datetime需用default参数转isoformat,反序列化需object_hook手动还原;避免__dict__陷阱,推荐to_dict方法;中文输出须设ensure_ascii=False和indent。

json.dumps 序列化字典时遇到 datetime 报错
直接用 json.dumps() 处理含 datetime 对象的字典会抛出 TypeError: Object of type datetime is not JSON serializable。Python 标准库不支持原生序列化日期时间类型。
解决方法是传入 default 参数,提供一个可调用对象来转换非标准类型:
import json from datetime import datetimedata = {"name": "Alice", "created_at": datetime.now()}
json_str = json.dumps(data, default=lambda obj: obj.isoformat() if hasattr(obj, 'isoformat') else None)
立即学习“Python免费学习笔记(深入)”;
输出类似:{"name": "Alice", "created_at": "2024-05-20T14:23:11.123456"}
- 优先用
obj.isoformat(),它比str(obj)更规范、可逆性强 - 避免在
default中无条件返回str(obj),否则可能把意外类型(如自定义类、bytes)也转成字符串,掩盖真正问题 - 如果需要 UTC 时间,记得先调用
obj.astimezone(timezone.utc)再isoformat()
用 json.loads 反序列化后还原 datetime 对象
json.loads() 默认只返回 dict、list、str 等基础类型,不会自动把 ISO 格式字符串变回 datetime。得靠 object_hook 手动识别和转换。
常见做法是在加载时扫描 key 名或值格式:
import json from datetime import datetimedef datetime_hook(d): for k, v in d.items(): if k == "created_at" and isinstance(v, str): try: d[k] = datetime.fromisoformat(v.replace("Z", "+00:00")) except ValueError: pass return d
data = json.loads('{"name": "Alice", "created_at": "2024-05-20T14:23:11.123456"}', object_hook=datetime_hook)
- 不要全局匹配所有字符串字段——容易误转 ID、手机号等纯数字字符串
-
fromisoformat()支持带Z的 UTC 表示,但需手动替换为+00:00,否则报错 - 若字段名不固定(比如日志中多个时间字段),可用正则匹配
r"^(at|time|date|ts)$"这类后缀
序列化自定义类实例时绕过 __dict__ 陷阱
直接对类实例调用 json.dumps(obj) 会失败;有人习惯写 json.dumps(obj.__dict__),但这有隐患:丢失方法、忽略私有属性(_attr)、跳过 property 计算属性、无法处理循环引用。
更可控的方式是显式定义导出逻辑:
class User:
def __init__(self, name, joined_at):
self.name = name
self.joined_at = joined_at
self._internal_id = 123 # 不应被序列化
def to_dict(self):
return {
"name": self.name,
"joined_at": self.joined_at.isoformat(),
"is_active": True # 可加入计算字段
}user = User("Bob", datetime.now())
json.dumps(user.to_dict())
- 别依赖
__dict__,尤其当类用了__slots__或 dataclass +__post_init__时,__dict__可能为空或不完整 - 如果类结构复杂且多处复用,可封装一个通用
as_json()方法,内部统一处理 datetime、枚举、嵌套对象 - 想保持接口简洁,也可重载
__json__()方法(非标准,但部分工具链识别),不过仍推荐显式to_dict()
处理中文、特殊字符与缩进时的编码细节
默认情况下 json.dumps() 会将非 ASCII 字符(如中文)转义为 \uXXXX,且输出无空格。这不利于调试和人工阅读。
两个关键参数必须明确设置:
json.dumps(data, ensure_ascii=False, indent=2)
-
ensure_ascii=False是硬性要求,否则中文变\u4f60\u597d,后续系统若没正确 decode 就乱码 -
indent=2仅用于开发或日志,线上 API 响应建议保持默认(无缩进),减少传输体积 - 若要兼容旧版 Python(default 返回
date对象——json模块不认它,得先转成datetime或字符串
最易被忽略的是时区信息:没有显式标注时区的 datetime 对象,isoformat() 输出不带 offset,反序列化后仍是“本地时间”语义,跨系统传递时极易出错。宁可全链路强制用 UTC 并显式标注。











