我正在将 Pydantic v1 代码库升级到 Pydantic V2。
Pydantic 有多种方法可以为任意 python 对象(即不从 BaseModel 等基本 pydantic 成员继承的类的实例)创建自定义序列化逻辑
但是,v1 Config.json_encoder 模式的弃用带来了一些挑战。
也就是说,客户端 BaseModel 类可以以多种方式使用任意 Python 类 Animal,只需声明一个 json 编码器。 示例:
class Animal:
def __init__(self, name):
self.name = name
class Zoo(BaseModel):
first_animal: Animal
kennel: Dict[str, Animal]
class Config:
json_encoders = {
Animal: lambda v: "standard critter"
}
其优点是确保模型中的任何 Animal 类型属性都可以使用提供的函数进行序列化。
下面的示例让我可以使用自定义逻辑来 json 序列化 Rock 类型成员的实例(RockBase 类已经具有当前代码库使用的类似 pydantic 的属性):
from typing import Annotated
from pydantic.functional_serializers import PlainSerializer
from pydantic import BaseModel, ConfigDict
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import CoreSchema, core_schema
class RockBase:
def __init__(self, value):
self.value = value
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
return core_schema.general_plain_validator_function(cls.validate)
@classmethod
def __get_pydantic_json_schema__(
cls, _core_schema: CoreSchema, handler: GetJsonSchemaHandler
) -> Dict[str, Any]:
extra_json_base = {"type": "string"}
return extra_json_base
@classmethod
def validate(cls, v=None, *args, **kwargs):
# validation logic
return v
Rock = Annotated[
RockBase,
PlainSerializer(lambda x: "a_string", return_type=str, when_used="always")
]
class SimpleAsteroid(BaseModel):
contains: Rock
model_config = ConfigDict(arbitrary_types_allowed=True)
mm1 = SimpleAsteroid(contains=Rock("gravel"))
print(mm1.model_dump_json())
但是,我正在使用的代码库包含几个 pydantic 模型,它们可以实例化任意嵌套结构。
例如,此调用也应该产生可序列化的对象:
SimpleAsteroid(contains={"extra":{"nested":{"value": Rock("it's deep")}}})
我可以声明以下模型来进行实例化:
class SimpleAsteroid(BaseModel):
contains: Any
model_config = ConfigDict(arbitrary_types_allowed=True)
但是序列化是不可能的,因为对 model_dump_json() 的调用将失败
pydantic_core._pydantic_core.PydanticSerializationError:无法序列化未知类型:类'__main__.RockBase'
有没有办法让序列化逻辑在RockBase基类中可用,并让pydantic根据需要发现它?
似乎实现 Pydantic 的人意识到
json_encoders
和新的序列化逻辑之间没有 1:1 对应关系,他们暂时恢复了 json_encoders
选项(我正在查看 2.5 版本)