如何序列化在 pydantic 中扩展内置类型的自定义类型?

Vu *_*ung 8 python serialization json pydantic

目前我正在使用 FastAPI 和 pydantic 作为序列化器。问题是,我们在服务器端使用雪花 id,这意味着我们需要在发送到客户端(javascript)之前将这些 id 转换为字符串,因为 id 大于 JS 的MAX SAFE INTEGER

所以我尝试创建一个新类来扩展 python 的 int 类型并自定义它的序列化和反序列化方式。这是我的代码:

class SnowflakeId(int):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v: str):
        return int(v)

    @classmethod
    def __modify_schema__(cls, field_schema: dict) -> None:
        field_schema['type'] = 'string'
Run Code Online (Sandbox Code Playgroud)

这是模型:

class BaseModel(pydantic.BaseModel):
    __abstract__ = True

    id: SnowflakeId

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True
        json_encoders = {
            SnowflakeId: lambda v: str(v)
        }
        alias_generator = camelize
        allow_population_by_field_name = True
Run Code Online (Sandbox Code Playgroud)

当从 json 字符串反序列化为 int id 时,它工作得很好,但是,当涉及到序列化时,输出仍然是整数。我希望它也将 id 序列化为字符串,这可能吗?

use*_*462 4

是的!

json_encoders是一个很好的尝试,但是在底层 pydantic调用 json.dumps。因此,对于可序列化类型(例如您的SnowflakeId),它不会关心额外的json_encoders.

你可以做的是重写 dumps 方法:

def my_dumps(v, *, default):
    for key, value in v.items():
        if isinstance(value, SnowflakeId):
            v[key] = str(value)
        else:
            v[key] = value
    return json.dumps(v)

class BaseModel(pydantic.BaseModel):
    id: SnowflakeId

    class Config:
        json_dumps = my_dumps
Run Code Online (Sandbox Code Playgroud)

让我们validate返回SnowflakeId

class SnowflakeId(int):
    ...

    @classmethod
    def validate(cls, v: str):
        return cls(v)
Run Code Online (Sandbox Code Playgroud)
m = BaseModel(id="123")
print(m.json()) # {"id": "123"}
Run Code Online (Sandbox Code Playgroud)