Search code examples
pythonpydanticpydantic-v2

Pydantic BaseModel validation order for Json vs str


Can someone tell me why Pydantic is validating a field as a string even though the field type is Json[Any] | str? And is there a way to have it return a dict instead?

from typing import Any

from pydantic import BaseModel, Json


class FooStr(BaseModel):
    json_or_str: Json[Any] | str


class FooInt(BaseModel):
    json_or_int: Json[Any] | int


if __name__ == "__main__":
    print(type(FooStr(json_or_str='{"a": 1}').json_or_str))  # prints <class 'str'>
    print(type(FooInt(json_or_int='{"a": 1}').json_or_int))  # prints <class 'dict'>

Solution

  • Thanks to Karl for the link to the documentation for unions in Pydantic.

    I needed union mode left to right. Pydantic defaults to smart mode which will look for the most exact match. In my case a str was a more exact match than parsing the string into a Json.

    Here's the working code:

    from typing import Any
    
    from pydantic import BaseModel, Field, Json
    
    
    class FooStr(BaseModel):
        json_or_str: Json[Any] | str = Field(union_mode="left_to_right")
    
    
    if __name__ == "__main__":
        print(type(FooStr(json_or_str='{"a": 1}').json_or_str))  # prints <class 'dict'>