pythonjson序列化_dumps与loads处理字典、类与日期时间技巧

8次阅读

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

pythonjson序列化_dumps与loads处理字典、类与日期时间技巧

json.dumps 序列化字典时遇到 datetime 报错

直接用 json.dumps() 处理含 datetime 对象的字典会抛出 TypeError: Object of type datetime is not JSON serializablepython 标准库不支持原生序列化日期时间类型。

解决方法是传入 default 参数,提供一个可调用对象来转换非标准类型:

import json from datetime import datetime 

data = {"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() 默认只返回 dictliststr 等基础类型,不会自动把 ISO 格式字符串变回 datetime。得靠 object_hook 手动识别和转换。

常见做法是在加载时扫描 key 名或值格式:

import json from datetime import datetime 

def 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 是硬性要求,否则中文变 u4f60u597d,后续系统若没正确 decode 就乱码
  • indent=2 仅用于开发或日志,线上 API 响应建议保持默认(无缩进),减少传输体积
  • 若要兼容旧版 Python(default 返回 date 对象——json 模块不认它,得先转成 datetime 或字符串

最易被忽略的是时区信息:没有显式标注时区的 datetime 对象,isoformat() 输出不带 offset,反序列化后仍是“本地时间”语义,跨系统传递时极易出错。宁可全链路强制用 UTC 并显式标注。

text=ZqhQzanResources