Search code examples
pythonjsondictionarypydantic

JSON dumping a dictionary where some values are PyDantic models


Assume a dictionary with an arbitrary structure, where some values are native Python objects and others are instances of PyDantic's BaseModel subclasses

e.g.

my_dict = {"key1": "value1",
           "key2": {"key3": pydantic_object1, 
                    "key4": 4},
           "key5": pydantic_object2}

I want to dump this dictionary to a JSON file. The naive solution of using BaseModel.model_dump() to convert first all the PyDantic objects to dictionaries and then using json.dump doesn't work, because some of the attributes of the PyDantic objects cannot be serialized by the native serializer, e.g. datetimess and other custom objects which their serializers are attached to the object's implementation.

I also couldn't figure out how to write a custom encoder that will user PyDantic's built in JSON encoder.

How would you solve this (PyDantic v2 and above)


Solution

  • Came across this question due to a similar problem (where I had a dictionary of BaseModels), which I solved with TypeAdapter:

    class SomePydanticClass1(BaseModel):
        some_int: int
        some_string: str
    
    test: Dict[str, SomePydanticClass1] = get_dict()
    
    with open("test.json", "wb") as test_json:
        ta = TypeAdapter(Dict[str, SomePydanticClass1])
        test_json.write(ta.dump_json(test, indent=4))
    

    Since in your case the dictionary would contain arbitrary types, I tested the following code:

    class SomePydanticClass1(BaseModel):
        some_int: int
        some_string: str
    
    
    class SomePydanticClass2(BaseModel):
        some_float: float
        some_string: str
    
    spc1 = SomePydanticClass1(some_int=1, some_string="foo")
    spc2 = SomePydanticClass2(some_float=2.3, some_string="bar")
    
    my_dict = {
        "key1": "value1",
        "key2": {"key3": spc1, "key4": 4},
        "key5": spc2,
    }
    
    with open("test.json", "wb") as test_json:
        ta = TypeAdapter(Dict[str, Any])
        test_json.write(ta.dump_json(my_dict, indent=4))
    

    which puts out the following json file:

    {
        "key1": "value1",
        "key2": {
            "key3": {
                "some_int": 1,
                "some_string": "foo"
            },
            "key4": 4
        },
        "key5": {
            "some_float": 2.3,
            "some_string": "bar"
        }
    }