Reputation: 383
currently I'm working with FastAPI and pydantic as serializer. Problem is, we're using snowflake id on the server side, which means we need to convert those ids to string before sending to client (javascript) because the id is larger than JS's MAX SAFE INTEGER.
So I tried to create a new class which extends python's int type and customize how it will be serialized and deserialized. Here's my code:
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'
And here is the model:
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
It works fine when deserializing from json string into int id, however, when it comes to the serialization, the output still is integer. I want it to serialize the id into string also, is it possible?
Upvotes: 9
Views: 6722
Reputation: 7158
Yes it is!
json_encoders
is a good try, however under the hood pydantic calls json.dumps. So for serializable types (like your SnowflakeId
) it won't care about additional json_encoders
.
What you can do is to override dumps method:
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
And let validate
return SnowflakeId
:
class SnowflakeId(int):
...
@classmethod
def validate(cls, v: str):
return cls(v)
m = BaseModel(id="123")
print(m.json()) # {"id": "123"}
Upvotes: 4