marshmallow本身不自动序列化复杂对象,只处理字典或基本类型;传入自定义类实例会因内部调用json.dumps()而报“object is not json serializable”错误,需通过attribute、pre_dump或as_dict等方式预处理为可序列化结构。

Marshmallow 本身不处理对象序列化,它只做数据验证与序列化/反序列化——关键在于你传给它的必须是字典或基本类型,不是任意复杂对象。直接传入自定义类实例会报 TypeError: Object of type XXX is not JSON serializable。
为什么 schema.dump(obj) 报错 “Object is not JSON serializable”
因为 dump() 内部调用的是 Python 的 json.dumps(),而 Marshmallow 的字段(如 String()、Nested())只负责从输入数据中提取、转换、校验字段,不会自动把一个未定义 __dict__ 或 to_dict() 方法的类实例“拍平”成字典。
常见错误场景:
- 直接传入 SQLAlchemy 模型实例,但没配置
SQLAlchemyAutoSchema或没定义fields - 传入 dataclass 实例,但没加
@post_dump或没手动转成 dict - 嵌套了非 dict / list / primitive 的对象(比如 datetime 是例外,有
DateTime()字段支持;但自定义类不是)
让自定义类被 schema.dump() 正确处理的三种方式
核心思路:确保 schema 能从对象上读到字段值。优先级从高到低:
-
显式定义字段 +
attribute参数:适用于字段名和属性名不一致,或需调用方法 -
在类中实现
__getattr__或提供as_dict()并配合post_dump:适合已有成熟模型类,不想改 schema 定义 -
用
pre_dump预处理对象为 dict:最灵活,但容易掩盖字段映射逻辑
示例(推荐第一种):
from marshmallow import Schema, fields <p>class User: def <strong>init</strong>(self, id, name, profile): self.id = id self.name = name self.profile = profile # 假设是另一个对象</p><p>class ProfileSchema(Schema): bio = fields.String()</p><p>class UserSchema(Schema): id = fields.Integer() name = fields.String() bio = fields.String(attribute="profile.bio") # 直接链式取属性 </p>
注意:attribute="profile.bio" 仅在 profile 是普通对象且有 bio 属性时有效;若 profile 是 None,会抛 AttributeError,需配合 load_only 或 allow_none=True 控制。
Nested 字段怎么正确关联子对象?
Nested 不是“自动递归序列化”,它只在输入是 dict 或有对应字段可提取时才生效。如果传入的是子类实例,必须保证该实例能被子 schema 的字段成功读取。
- 子 schema 必须明确定义字段,不能只靠
Nested(UserSchema)就指望它猜出怎么取值 - 若子对象字段名和 schema 字段名不一致,用
attribute或data_key显式绑定 - 避免循环引用:A 包含 B,B 又包含 A → 用
lazy="joined"(SQLAlchemy)或延迟加载Nested(如Nested(lambda: UserSchema()))
错误写法:
class UserSchema(Schema):
profile = fields.Nested(ProfileSchema) # ❌ profile 是 Profile 实例,但 ProfileSchema 没定义字段来读它
正确写法:
class ProfileSchema(Schema):
bio = fields.String(attribute="bio") # 明确告诉它从 profile.bio 读
<p>class UserSchema(Schema):
profile = fields.Nested(ProfileSchema, attribute="profile") # ✅
</p>
性能与调试:什么时候该放弃 Marshmallow 直接用 pydantic?
如果你的“复杂对象”大量依赖动态属性、__getattr__、描述符、或需要深度嵌套 + 自动模型发现(比如 FastAPI 默认行为),Marshmallow 的显式字段绑定反而成为负担。这时 pydantic.BaseModel 的 .model_dump() 更自然。
但别轻易切换——已用 Marshmallow 的项目里,pre_dump + asdict()(对 dataclass)或 __dict__(对简单类)往往比重构成 pydantic 更省事。
最容易被忽略的一点:schema.dump() 返回的是 dict,不是 JSON 字符串。要输出 JSON,还得自己 json.dumps(result);而很多人卡在“为什么返回值打印出来是 dict 却不能直接给前端”,其实是漏了这步。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











