pydantic Deserialization pit: TypeError: Object of type 'datetime' is not JSON serializable

Problem Description

There are the following requirements:

 import requests

requests.post('http://localhost/upload/', json=data)

data should be a dict type

The value of data should not have detetime type, if there is datetime it should be converted to str

 from datetime import datetime, timezone
from datetime import datetime
from pydantic import BaseModel, Field
import requests
import json


def get_utc_now_timestamp() -> datetime:
    return datetime.utcnow().replace(tzinfo=timezone.utc)


class Struct(BaseModel):
    author: str = Field(...)
    releaseDate: datetime = Field(...)


data = Struct(
    author='jike',
    releaseDate=get_utc_now_timestamp()
)


requests.post('http://localhost/upload/', json=data.dict())

The above code will report an error: TypeError: Object of type ‘datetime‘ is not JSON serializable

why? Because data should be a dict, the value of releaseDate should be converted to str, not datetime

And pydantic's dict() will only convert the value of releaseDate to datetime instead of str

So, we need to modify the code:

 from datetime import datetime, timezone
from datetime import datetime
from pydantic import BaseModel, Field
import requests
import json


def get_utc_now_timestamp() -> datetime:
    return datetime.utcnow().replace(tzinfo=timezone.utc)


class Struct(BaseModel):
    author: str = Field(...)
    releaseDate: datetime = Field(...)


data = Struct(
    author='jike',
    releaseDate=get_utc_now_timestamp()
).json(ensure_ascii=False)


requests.post('http://localhost/upload/', json=json.loads(data))

The json of the pydantic model will convert the datetime type field to str

The solution is:

  • First call the json method of pydantic to serialize the object into a string. ( pydantic object -> str )
  • Deserialize the string into a dict dictionary by calling the loads method of the json library. ( str->dict )

It's very convoluted, isn't it? No way!

Of course, if you don't want to use it in requests, but just want to serialize it into a json string, you can use the pydantic prepared for you pydantic_encoder , the usage is as follows:

 from datetime import datetime, timezone
from datetime import datetime
from loguru import logger
from pydantic import BaseModel, Field
import json
from pydantic.json import pydantic_encoder

def get_utc_now_timestamp() -> datetime:
    return datetime.utcnow().replace(tzinfo=timezone.utc)


class Struct(BaseModel):
    author: str = Field(...)
    releaseDate: datetime = Field(...)


data = Struct(
    author='jike',
    releaseDate=get_utc_now_timestamp()
)


logger.debug(json.dumps(data.dict(),default=pydantic_encoder))

universe_king
3.4k 声望678 粉丝