Skip the Class Inheritance

It is important to note that the main purpose of sub-classing from JSONWizard Mixin class is to provide helper methods like from_dict() and to_dict(), which makes it much more convenient and easier to load or dump your data class from and to JSON.

That is, it’s meant to complement the usage of the dataclass decorator, rather than to serve as a drop-in replacement for data classes, or to provide type validation for example; there are already excellent libraries like pydantic that provide these features if so desired.

However, there may be use cases where we prefer to do away with the class inheritance model introduced by the Mixin class. In the interests of convenience and also so that data classes can be used as is, the Dataclass Wizard library provides the helper functions fromlist() and fromdict() for de-serialization, and asdict() for serialization. These functions also work recursively, so there is full support for nested dataclasses – just as with the class inheritance approach.

Here is an example to demonstrate the usage of these helper functions:

from dataclasses import dataclass
from datetime import datetime
from typing import List, Optional, Union

from dataclass_wizard import fromdict, asdict, DumpMeta


@dataclass
class Container:
    id: int
    created_at: datetime
    my_elements: List['MyElement']


@dataclass
class MyElement:
    order_index: Optional[int]
    status_code: Union[int, str]


source_dict = {'id': '123',
               'createdAt': '2021-01-01 05:00:00Z',
               'myElements': [
                   {'orderIndex': 111, 'statusCode': '200'},
                   {'order_index': '222', 'status_code': 404}
               ]}

# De-serialize the JSON dictionary object into a `Container` instance.
c = fromdict(Container, source_dict)

print(repr(c))
# prints:
#   Container(id=123, created_at=datetime.datetime(2021, 1, 1, 5, 0), my_elements=[MyElement(order_index=111, status_code='200'), MyElement(order_index=222, status_code=404)])

# (Optional) Set up dump config for the inner class, as unfortunately there's
# no option currently to have the meta config apply in a recursive fashion.
_ = DumpMeta(MyElement, key_transform='SNAKE')

# Serialize the `Container` instance to a Python dict object with a custom
# dump config, for example one which converts field names to snake case.
json_dict = asdict(c, DumpMeta(Container,
                               key_transform='SNAKE',
                               marshal_date_time_as='TIMESTAMP'))

expected_dict = {'id': 123,
                 'created_at': 1609477200,
                 'my_elements': [
                     {'order_index': 111, 'status_code': '200'},
                     {'order_index': 222, 'status_code': 404}
                 ]}

# Assert that we get the expected dictionary object.
assert json_dict == expected_dict