pythonjsonserializationenumspydantic

How to JSON serialize ENum classes in Pydantic BaseModel


I have the following code that uses Pydantic BaseModel data class

from enum import Enum

import requests
from pydantic import BaseModel
from requests import Response


class PetType(Enum):
    DOG: str = 'dog'
    CAT: str = 'cat'


class Pet(BaseModel):
    name: str
    type: PetType


my_dog: Pet = Pet(name='Lucky', type=PetType.DOG)

# This works
resp: Response = requests.post('https://postman-echo.com/post', json=my_dog.json())
print(resp.json())

#This doesn't work
resp: Response = requests.post('https://postman-echo.com/post', json=my_dog.dict())
print(resp.json())

That when I send json equals to model's dict(), I get the error:

TypeError: Object of type 'PetType' is not JSON serializable

How do I overcome this error and make PetType also serializable?

P.S. The above example is short and simple, but I hit a use case where both cases of sending

json=my_dog.json() 

and

json=my_dog.dict() 

don't work. This is why I need to solve sending using dict().


Solution

  • **<---- Addition 2 ----> **

    Check types like https://docs.python.org/3/library/enum.html#enum.StrEnum and https://docs.python.org/3.12/library/enum.html#enum.IntEnum

    Instead of MyEnum(str, Enum) use MyEnum(StrENum)

    **<---- Addition ----> **

    Look for Pydantic's parameter "use_enum_values" in Pydantic Model Config

    use_enum_values whether to populate models with the value property of enums, rather than the raw enum. This may be useful if you want to serialise model.dict() later (default: False)

    It looks like setting this value to True will do the same as the below solution.


    Turns out that this is a behavior of ENum, which is discussed here: https://github.com/samuelcolvin/pydantic/issues/2278

    The way you should define the enum is using

    class PetType(str, Enum):
    

    instead of

    class PetType(Enum):
    

    For integers this Python's Enum library provides the type IntEnum: https://docs.python.org/3.10/library/enum.html#enum.IntEnum

    which is basically

    class IntEnum(int, Enum):
        pass
    

    If you look at the above Enum documentation you will find that a type like StrEnum doesn't exist but following the example for PetType you can define it easily.

    I am attaching the working code below

    from enum import Enum
    
    import requests
    from pydantic import BaseModel
    from requests import Response
    
    
    class PetType(str, Enum):
        DOG: str = 'dog'
        CAT: str = 'cat'
    
    
    class Pet(BaseModel):
        name: str
        type: PetType
    
    
    my_dog: Pet = Pet(name='Lucky', type=PetType.DOG)
    
    # This works
    resp: Response = requests.post('https://postman-echo.com/post', json=my_dog.json())
    print(resp.json())
    
    # Now this also works
    resp: Response = requests.post('https://postman-echo.com/post', json=my_dog.dict())
    print(resp.json())