V1: Recursive Types and Dataclasses with Cyclic References - rnag/dataclass-wizard GitHub Wiki

Starting with version 0.34.0, recursive types are supported out of the box (OOTB) with the v1 opt-in, eliminating the need for Meta settings like recursive_classes = True.

This makes working with recursive dataclasses even easier and more streamlined. In addition, recursive types are now supported for the following Python type constructs:

Example Usage

Recursive types allow handling complex nested data structures, such as deeply nested JSON objects or lists. With v0.34.0 of Dataclass Wizard, de/serializing these structures becomes seamless and more intuitive.

Recursive Union

First, define the Type Alias JSON.

Python 3.9

For Python 3.9, use this Union approach:

from typing_extensions import TypeAlias
JSON: TypeAlias = 'str | int | float | bool | dict[str, JSON] | list[JSON] | None'

Python 3.10 and 3.11

For Python 3.10 and above, use this simpler approach:

JSON = str | int | float | bool | dict[str, 'JSON'] | list['JSON'] | None

Python 3.12+

For Python 3.12+, you can use the type statement:

type JSON = str | int | float | bool | dict[str, JSON] | list[JSON] | None

Usage

from dataclasses import dataclass
from dataclass_wizard import JSONWizard

@dataclass
class MyTestClass(JSONWizard):

    class _(JSONWizard.Meta):
        v1 = True

    name: str
    meta: str
    msg: JSON

x = MyTestClass.from_dict(
    {
        "name": "name",
        "meta": "meta",
        "msg": [{"x": {"x": [{"x": ["x", 1, 1.0, True, None]}]}}],
    }
)
assert x == MyTestClass(
    name="name",
    meta="meta",
    msg=[{"x": {"x": [{"x": ["x", 1, 1.0, True, None]}]}}],
)

[!NOTE] The type statement in Python 3.12+ simplifies type alias definitions by avoiding string annotations for recursive references.

Recursive Union with Nested dataclasses

from dataclasses import dataclass, field
from dataclass_wizard import JSONWizard

@dataclass
class A(JSONWizard):

    class _(JSONWizard.Meta):
        v1 = True

    value: int
    nested: 'B'
    next: 'A | None' = None

@dataclass
class B:
    items: list[A] = field(default_factory=list)

x = A.from_dict(
    {
        "value": 1,
        "next": {"value": 2, "next": None, "nested": {}},
        "nested": {"items": [{"value": 3, "nested": {}}]},
    }
)
assert x == A(
    value=1,
    next=A(value=2, next=None, nested=B(items=[])),
    nested=B(items=[A(value=3, nested=B())]),
)

[!NOTE] Nested dataclasses are particularly useful for representing hierarchical structures, such as trees or graphs, in a readable and maintainable way.