pythonpython-dataclassespydantic

Initializing a pydantic dataclass from json


I'm in the process of converting existing dataclasses in my project to pydantic-dataclasses, I'm using these dataclasses to represent models I need to both encode-to and parse-from json.

Here's an example of my current approach that is not good enough for my use case, I have a class A that I want to both convert into a dict (to later be converted written as json) and to read from that dict. But the only way I can find to parse the json back into a model gives me back the underlying BaseModel and not the dataclass.

note that I'm using the asdict function to convert the dataclass to a dict as it's what the pydantic_encoder uses to convert the dataclass to json, and using the pydantic_encoder what the documentation recommends to convert a pydantic-dataclass to json: https://pydantic-docs.helpmanual.io/usage/dataclasses/

from dataclasses import asdict
from pydantic.dataclasses import dataclass
from pydantic import BaseModel

@dataclass
class A:
    x: str

a = A("string")
a_dict = asdict(a)
parsed_a = A.__pydantic_model__.parse_obj(a_dict)

print(f"type of a: {type(a)}")
print(f"type of parsed_a: {type(parsed_a)}")

print(f"a is instance of A: {isinstance(a, A)}")
print(f"parsed_a is instance of A: {isinstance(parsed_a, A)}")

print(f"a is instance of BaseModel: {isinstance(a, BaseModel)}")
print(f"parsed_a is instance of BaseModel: {isinstance(parsed_a, BaseModel)}")

output:

type of a: <class '__main__.A'>
type of parsed_a: <class '__main__.A'>
a is instance of A: True
parsed_a is instance of A: False
a is instance of BaseModel: False
parsed_a is instance of BaseModel: True

Is there maybe a way to initialize A from the parsed BaseModel?


Solution

  • To build on others' answers, since the right/best answer to this appears to constantly change:

    Using Pydantic's TypeAdapter is currently the way to go. Updated @pceccon example:

    from pydantic.dataclasses import dataclass
    from pydantic import TypeAdapter
    import dataclasses
    import json
    
    @dataclass
    class User:
      id: int
      name: str
    
    user = User(id=123, name="James")
    user_json = json.dumps(dataclasses.asdict(user))
    print(user_json)  # '{"id": 123, "name": "James"}'
    
    user = TypeAdapter(User).validate_json(user_json)
    print(user)  # User(id=123, name='James')
    print(user.name)  # 'James'
    

    if you want PyCharm (or whatever editor) to give you autocompletion/intellisense, use a type hint:

    user: User = TypeAdapter(User).validate_json(user_json)
    

    if you have a dictionary instead of json, you can use TypeAdapter.validate_python instead:

    user: User = TypeAdapter(User).validate_python(user_dict)
    
    

    This works recursively for your model/class definitions.