Pydantic: json_encoders doesn't work for custom Types?

Created on 30 Jun 2020  路  1Comment  路  Source: samuelcolvin/pydantic

Question

This is taken from the docs, I want to serialize DayThisYear using %Y%m%d format, but it seems that the encoder doesn't work for custom types?

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.5.1
            pydantic compiled: True
                 install path: /Users/christian/Library/Caches/pypoetry/virtualenvs/keepa-products-NnnmQlTQ-py3.8/lib/python3.8/site-packages/pydantic
               python version: 3.8.2 (default, May 19 2020, 18:30:57)  [Clang 11.0.0 (clang-1100.0.33.17)]
                     platform: macOS-10.15.4-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']
from typing import Optional
from datetime import date, timedelta
from pydantic import BaseModel
from pydantic.validators import int_validator

class DayThisYear(date):
    """
    Contrived example of a special type of date that
    takes an int and interprets it as a day in the current year
    """
    @classmethod
    def __get_validators__(cls):
        yield int_validator
        yield cls.validate

    @classmethod
    def validate(cls, v: int):
        return date.today().replace(month=1, day=1) + timedelta(days=v)


class FooModel(BaseModel):
    date1: DayThisYear
    date2: Optional[DayThisYear]

    class Config:
        json_encoders = {
            DayThisYear: lambda v: v.strftime("%Y%m%d") if v else None,
        }

m = FooModel(date1=300, date2=300)
print(m.json())
#>{"date1": "2020-10-27", "date2": "2020-10-27"}

...
question

Most helpful comment

Hello @chespinoza,

Indeed the behaviour is this one as you return datetime.date objects in your validate method. So the dict that is dumped as a JSON is {'date1': datetime.date(2020, 10, 27), 'date2': datetime.date(2020, 10, 27)}.
If you change your code to return DayThisYear instances like this

    @classmethod
    def validate(cls, v: int):
        return cls.today().replace(month=1, day=1) + timedelta(days=v)

then the dict will be {'date1': DayThisYear(2020, 10, 27), 'date2': DayThisYear(2020, 10, 27)}
and you will have the expected JSON.

>All comments

Hello @chespinoza,

Indeed the behaviour is this one as you return datetime.date objects in your validate method. So the dict that is dumped as a JSON is {'date1': datetime.date(2020, 10, 27), 'date2': datetime.date(2020, 10, 27)}.
If you change your code to return DayThisYear instances like this

    @classmethod
    def validate(cls, v: int):
        return cls.today().replace(month=1, day=1) + timedelta(days=v)

then the dict will be {'date1': DayThisYear(2020, 10, 27), 'date2': DayThisYear(2020, 10, 27)}
and you will have the expected JSON.

Was this page helpful?
0 / 5 - 0 ratings