python 标准库的json模块默认无法直接序列化自定义的dataclass实例(会报object of type xxx is not json serializable),核心原因是json只支持基础类型(str/int/list/dict 等)。下面我会从基础到进阶给出 4 种优雅的转换方法,覆盖简单场景到生产级复杂场景,你可根据需求选择。
前置准备:定义测试数据类
先定义一个典型的嵌套数据类,作为后续转换的示例:
from dataclasses import dataclass, asdict
import json
from datetime import datetime # 包含非基础类型,模拟真实场景
# 嵌套子数据类
@dataclass
class address:
province: str
city: str
detail: str = "" # 可选字段
# 主数据类
@dataclass
class user:
id: int
name: str
age: int
register_time: datetime # 非json原生类型
address: address # 嵌套数据类
tags: list[str] = none # 可选列表字段
# 创建测试实例
test_user = user(
id=1001,
name="张三",
age=28,
register_time=datetime(2026, 1, 19, 10, 30),
address=address(province="浙江省", city="杭州市", detail="西湖区xx路"),
tags=["vip", "新用户"]
)
方法 1:基础版 -dataclasses.asdict手动转换(简单场景)
这是最基础的优雅方式,利用dataclasses内置的asdict方法将数据类转成字典,再用json.dumps序列化。
代码实现:
# 步骤1:将dataclass转成普通字典 user_dict = asdict(test_user) # 步骤2:处理非json原生类型(如datetime) user_dict["register_time"] = user_dict["register_time"].isoformat() # 步骤3:序列化为json字符串 user_json = json.dumps(user_dict, ensure_ascii=false, indent=2) print(user_json)
输出结果:
{
"id": 1001,
"name": "张三",
"age": 28,
"register_time": "2026-01-19t10:30:00",
"address": {
"province": "浙江省",
"city": "杭州市",
"detail": "西湖区xx路"
},
"tags": ["vip", "新用户"]
}
关键解释:
asdict(test_user):核心函数,将数据类实例递归转换为普通字典(嵌套的address也会被转成字典);ensure_ascii=false:保留中文等非 ascii 字符,避免转成\uxxxx编码;indent=2:格式化输出,提升可读性;- 需手动处理
datetime等非 json 原生类型(json 仅支持字符串 / 数字 / 布尔 / 列表 / 字典 /null)。
方法 2:进阶版 - 自定义jsonencoder(通用可扩展)
如果有多个数据类需要转换,手动处理非基础类型会很繁琐,推荐自定义jsonencoder,实现统一的序列化规则。
代码实现:
class dataclassjsonencoder(json.jsonencoder):
"""自定义json编码器,支持dataclass和常见非基础类型"""
def default(self, obj):
# 第一步:处理dataclass实例
if hasattr(obj, "__dataclass_fields__"): # 判断是否是dataclass
return asdict(obj)
# 第二步:处理datetime类型
if isinstance(obj, datetime):
return obj.isoformat()
# 第三步:其他类型交给父类处理(会抛出标准异常)
return super().default(obj)
# 直接使用自定义编码器序列化
user_json = json.dumps(
test_user,
cls=dataclassjsonencoder, # 指定自定义编码器
ensure_ascii=false,
indent=2
)
print(user_json)
关键解释:
- 继承
json.jsonencoder并重写default方法:这是json模块扩展序列化规则的标准方式; __dataclass_fields__:dataclass 实例的内置属性,用于判断是否为数据类;- 可扩展:如需支持
date/uuid等类型,只需在default方法中新增isinstance判断即可; - 无需手动转字典,直接序列化数据类实例,代码更简洁。
方法 3:优化版 - 封装成工具函数(复用性拉满)
将转换逻辑封装成工具函数,一键调用,适合项目中多处使用的场景:
def dataclass_to_json(obj, indent=2):
"""
数据类转json字符串的工具函数
:param obj: 数据类实例(支持嵌套)
:param indent: 格式化缩进,none表示紧凑输出
:return: json字符串
"""
return json.dumps(
obj,
cls=dataclassjsonencoder,
ensure_ascii=false,
indent=indent
)
# 调用示例(一行搞定)
user_json = dataclass_to_json(test_user)
print(user_json)
方法 4:高级版 - 使用pydantic(生产环境推荐)
如果你的场景需要数据验证 + 优雅序列化,推荐直接使用pydantic(python 生态中最主流的数据验证库),它的basemodel比原生dataclass更强大,且原生支持 json 转换。
前置依赖:
pip install pydantic
代码实现:
from pydantic import basemodel
from datetime import datetime
# 替换原生dataclass为pydantic.basemodel
class address(basemodel):
province: str
city: str
detail: str = ""
class user(basemodel):
id: int
name: str
age: int
register_time: datetime
address: address
tags: list[str] = none
# 可选:自定义json序列化配置(如datetime格式)
class config:
json_encoders = {
datetime: lambda v: v.strftime("%y-%m-%d %h:%m:%s") # 自定义时间格式
}
# 创建实例(pydantic会自动验证字段类型)
test_user = user(
id=1001,
name="张三",
age=28,
register_time=datetime(2026, 1, 19, 10, 30),
address=address(province="浙江省", city="杭州市", detail="西湖区xx路"),
tags=["vip", "新用户"]
)
# 原生支持json转换(一行搞定)
user_json = test_user.json(ensure_ascii=false, indent=2)
print(user_json)
输出结果(时间格式自定义后):
{
"id": 1001,
"name": "张三",
"age": 28,
"register_time": "2026-01-19 10:30:00",
"address": {
"province": "浙江省",
"city": "杭州市",
"detail": "西湖区xx路"
},
"tags": ["vip", "新用户"]
}
关键优势:
- 原生支持
json()方法:无需手动写编码器,开箱即用; - 自动数据验证:创建实例时会校验字段类型(如
age传字符串会直接报错); - 灵活配置:通过
config.json_encoders自定义非基础类型的序列化规则; - 支持嵌套、可选字段、默认值等,完全兼容原生 dataclass 的用法,且功能更强。
总结
- 简单场景:用
dataclasses.asdict+json.dumps,手动处理非基础类型,快速实现; - 通用场景:自定义
jsonencoder,统一管理序列化规则,适合多个数据类的项目; - 生产环境:优先使用
pydantic.basemodel,兼顾数据验证和优雅序列化,减少冗余代码; - 核心原则:避免手动拼接字典,利用内置工具 / 成熟库实现转换,保证代码的可维护性和扩展性。
以上就是python数据类转换为json的常用方法总结的详细内容,更多关于python数据类转json的资料请关注代码网其它相关文章!
发表评论