Simple, elegant wizarding tools for Python’s dataclasses.
📘 Docs → dcw.ritviknag.com
Dataclass Wizard is a fast, well-tested serialization library for Python dataclasses with automatic type conversion and support for JSON, YAML, TOML, and environment variables.
Behold, the power of Dataclass Wizard:
>>> from __future__ import annotations
>>> from dataclasses import field
>>> from dataclass_wizard import DataclassWizard
...
>>> class MyClass(DataclassWizard, load_case='AUTO', dump_case='CAMEL'):
... my_str: str | None
... is_active_tuple: tuple[bool, ...]
... list_of_int: list[int] = field(default_factory=list)
...
>>> MyClass.from_json(
... '{"my_str": 20, "ListOfInt": ["1", "2", 3], "isActiveTuple": [true, false, 1]}'
... )
MyClass(my_str='20', is_active_tuple=(True, False, True), list_of_int=[1, 2, 3])
Note
The example above demonstrates automatic type coercion, key casing
transforms, and support for nested dataclasses. DataclassWizard
also auto-applies @dataclass to subclasses.
Contents
- Why Dataclass Wizard?
- Quick Examples
- Installation
- Wizard Mixins ✨
- Supported Types 🧑💻
- Custom Type Hooks 🔌
- JSON Marshalling
- No Inheritance Needed
- Custom Key Mappings
- Mapping Nested JSON Keys
- Extending from
Meta - Date and Time with Custom Patterns
- Recursive Types and Dataclasses with Cyclic References
- Dataclasses in
UnionTypes - Supercharged
UnionParsing - Conditional Field Skipping
- Serialization Options
EnvironMagic- Field Properties
- Contributing
- TODOs
- Credits
Dataclass Wizard helps you turn Python dataclasses into robust, high-performance serialization schemas with minimal effort.
- 🚀 Fast — code-generated loaders and dumpers
- 🪶 Lightweight — pure Python, minimal dependencies
- 🧠 Typed — powered by Python type hints
- 🧙 Flexible — JSON, YAML, TOML, and environment variables
- 🧪 Reliable — battle-tested with extensive test coverage
JSON De/Serialization
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class Data(JSONWizard):
value: int
data = Data.from_dict({"value": "123"})
assert data.value == 123
print(data.to_json())Environment Variables
import os
from dataclass_wizard import EnvWizard
os.environ['DEBUG'] = 'true'
class Config(EnvWizard):
debug: bool
cfg = Config()
assert cfg.debug is TrueEnvWizard
import os
from dataclass_wizard import EnvWizard, Env
os.environ['DEBUG_MODE'] = 'true'
class Config(EnvWizard):
debug: bool = Env('DEBUG_MODE')
cfg = Config()
assert cfg.debug is TrueInstall from PyPI:
pip install dataclass-wizardOr via conda-forge:
conda install -c conda-forge dataclass-wizardDataclass Wizard supports Python 3.9+.
Note
Python 3.6–3.8 users should install the last supported release,
dataclass-wizard==0.26.1.
See PyPI for historical versions and the Changelog for details.
In addition to JSONWizard, Dataclass Wizard provides a set of focused
Mixin classes to simplify common serialization tasks:
- 🪄 EnvWizard — Load environment variables and
.envfiles into typed schemas, including support for secret directories. - 🎩 JSONPyWizard — A helper for
JSONWizardthat preserves keys as-is (no case transformations). - 🔮 JSONListWizard — Extend
JSONWizardto convert lists into container objects. - 💼 JSONFileWizard — Load and save dataclass instances from local JSON files.
Optional format support:
- 🌳 TOMLWizard — Map dataclasses to and from TOML.
- 🧙♂️ YAMLWizard — Convert between YAML and dataclasses using
PyYAML.
Dataclass Wizard supports:
- 📋 Collections: Handle
list,dict, andseteffortlessly. - 🔢 Typing Generics: Manage
Union,Any, and other types from the typing module. - 🌟 Advanced Types: Work with
Enum,defaultdict, anddatetimewith ease.
For more info, check out the Supported Types section in the docs for detailed insights into each type and the load/dump process!
Dataclass Wizard allows you to extend serialization and deserialization to support custom or unsupported types via type hooks.
Type hooks define how a value is:
- loaded (parsed) from JSON or a dictionary
- dumped (serialized) back into JSON-compatible data
This makes it easy to support standard-library types such as ipaddress.IPv4Address as well as user-defined classes.
Type hooks work with or without inheritance, and integrate seamlessly with the v1 code generation engine.
See Type Hooks for details and examples.
JSONWizard (or DataclassWizard which auto-applies @dataclass) is a Mixin class which
provides the following helper methods that are useful for serializing (and loading)
a dataclass instance to/from JSON, as defined by the AbstractJSONWizard
interface.
| Method | Example | Description |
|---|---|---|
from_json |
item = Product.from_json(string) | Converts a JSON string to an instance of the dataclass, or a list of the dataclass instances. |
from_list |
list_of_item = Product.from_list(l) | Converts a Python list object to a list of the
dataclass instances. |
from_dict |
item = Product.from_dict(d) | Converts a Python dict object to an instance
of the dataclass. |
to_dict |
d = item.to_dict() | Converts the dataclass instance to a Python dict
object that is JSON serializable. |
to_json |
string = item.to_json() | Converts the dataclass instance to a JSON string representation. |
list_to_json |
string = Product.list_to_json(list_of_item) | Converts a list of dataclass instances to a JSON string representation. |
Additionally, it adds a default __str__ method to subclasses, which will
pretty print the JSON representation of an object; this is quite useful for
debugging purposes. Whenever you invoke print(obj) or str(obj), for
example, it'll call this method which will format the dataclass object as
a prettified JSON string. If you prefer a __str__ method to not be
added, you can pass in str=False when extending from the Mixin class
as mentioned here.
Note that the __repr__ method, which is implemented by the
dataclass decorator, is also available. To invoke the Python object
representation of the dataclass instance, you can instead use
repr(obj) or f'{obj!r}'.
To mark a dataclass as being JSON serializable (and
de-serializable), simply sub-class from JSONWizard as shown
below.
Check out a more complete example of using the JSONWizard
Mixin class.
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:
Note
As of v0.18.0, the Meta config for the main dataclass will cascade down
and be merged with the Meta config (if specified) of each nested dataclass. To
disable this behavior, you can pass in recursive=False to the Meta config.
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, date
from dataclass_wizard import fromdict, asdict, DumpMeta
@dataclass
class A:
created_at: datetime
list_of_b: list[B] = field(default_factory=list)
@dataclass
class B:
my_status: int | str
my_date: date | None = None
source_dict = {'created_at': '2010-06-10 15:50:00Z',
'list_of_b': [
{'my_status': '200', 'my_date': '2021-12-31'}
]}
# De-serialize the JSON dictionary object into an `A` instance.
a = fromdict(A, source_dict)
print(repr(a))
# A(created_at=datetime.datetime(2010, 6, 10, 15, 50, tzinfo=datetime.timezone.utc),
# list_of_b=[B(my_status='200', my_date=datetime.date(2021, 12, 31))])
# Set an optional dump config for the main dataclass, for example one which
# converts converts date and datetime objects to a unix timestamp (as an int)
#
# Note that `recursive=True` is the default, so this Meta config will be
# merged with the Meta config (if specified) of each nested dataclass.
DumpMeta(dump_date_time_as='TIMESTAMP',
case='SNAKE',
# Finally, apply the Meta config to the main dataclass.
).bind_to(A)
# Serialize the `A` instance to a Python dict object.
json_dict = asdict(a)
expected_dict = {'created_at': 1276185000, 'list_of_b': [{'my_status': '200', 'my_date': 1640908800}]}
print(json_dict)
# Assert that we get the expected dictionary object.
assert json_dict == expected_dictIf you ever find the need to add a custom mapping of a JSON key to a dataclass
field (or vice versa), the helper function Alias -- which can be
considered an alias to dataclasses.field() -- is one approach that can
resolve this.
Example below:
from dataclass_wizard import DataclassWizard, Alias
class MyClass(DataclassWizard):
my_str: str = Alias('myString1')
# De-serialize a dictionary object with the newly mapped JSON key.
d = {'myString1': 'Testing'}
c = MyClass.from_dict(d)
print(repr(c))
# prints:
# MyClass(my_str='Testing')
# Assert we get the same dictionary object when serializing the instance.
assert c.to_dict() == dThe dataclass-wizard library allows you to map deeply nested JSON keys to dataclass fields using custom path notation. This is ideal for handling complex or non-standard JSON structures.
You can specify paths to JSON keys with the AliasPath helpers. For example, the deeply nested key data.items.myJSONKey can be mapped to a dataclass field, such as my_str:
from dataclass_wizard import AliasPath, DataclassWizard
class MyData(DataclassWizard):
my_str: str = AliasPath('data.items.myJSONKey', default="default_value")
input_dict = {'data': {'items': {'myJSONKey': 'Some value'}}}
data_instance = MyData.from_dict(input_dict)
print(data_instance.my_str) # Output: 'Some value'You can now use custom paths to access nested keys and map them to specific fields, even when keys contain special characters or follow non-standard conventions.
Example with nested and complex keys:
from dataclasses import dataclass
from typing import Annotated
from dataclass_wizard import JSONWizard, AliasPath
@dataclass
class NestedData(JSONWizard):
my_str: str = AliasPath('data[0].details["key with space"]', default="default_value")
my_int: Annotated[int, AliasPath('data[0].items[3.14].True')] = 0
input_dict = {
'data': [
{
'details': {'key with space': 'Another value'},
'items': {3.14: {True: "42"}}
}
]
}
# Deserialize JSON to dataclass
data = NestedData.from_dict(input_dict)
print(data.my_str) # Output: 'Another value'
# Serialize back to JSON
output_dict = data.to_dict()
print(output_dict) # {'data': {0: {'details': {'key with space': 'Another value'}, 'items': {3.14: {True: 42}}}}}
# Verify data consistency
assert data == NestedData.from_dict(output_dict)
# Handle empty input gracefully
data = NestedData.from_dict({'data': []})
print(repr(data)) # NestedData(my_str='default_value', my_int=0)Looking to change how date and datetime objects are serialized to JSON? Or
prefer that field names appear in snake case when a dataclass instance is serialized?
The inner Meta class allows easy configuration of such settings, as
shown below; and as a nice bonus, IDEs should be able to assist with code completion
along the way.
Note
As of v0.18.0, the Meta config for the main dataclass will cascade down
and be merged with the Meta config (if specified) of each nested dataclass. To
disable this behavior, you can pass in recursive=False to the Meta config.
from dataclasses import dataclass
from datetime import date
from dataclass_wizard import JSONWizard
from dataclass_wizard.enums import DateTimeTo
@dataclass
class MyClass(JSONWizard):
class _(JSONWizard.Meta):
dump_date_time_as = DateTimeTo.TIMESTAMP
dump_case = 'SNAKE'
my_str: str
my_date: date
data = {'my_str': 'test', 'my_date': '2010-12-30'}
c = MyClass.from_dict(data)
print(repr(c))
# prints:
# MyClass(my_str='test', my_date=datetime.date(2010, 12, 30))
string = c.to_json()
print(string)
# prints:
# {"my_str": "test", "my_date": 1293667200}Here are a few additional use cases for the inner Meta class. Note that
a full list of available settings can be found in the Meta section in the docs.
Added in v0.28.0
There is now Easier Debug Mode.
Enables additional (more verbose) log output. For example, a message can be
logged whenever an unknown JSON key is encountered when
from_dict or from_json is called.
This also results in more helpful error messages during the JSON load (de-serialization) process, such as when values are an invalid type -- i.e. they don't match the annotation for the field. This can be particularly useful for debugging purposes.
Note
There is a minor performance impact when DEBUG mode is enabled; for that reason, I would personally advise against enabling this in a production environment.
The default behavior is to ignore any unknown or extraneous JSON keys that are
encountered when from_dict or from_json is called, and emit a "warning"
which is visible when debug mode is enabled (and logging is properly configured).
An unknown key is one that does not have a known mapping to a dataclass field.
However, we can also raise an error in such cases if desired. The below example demonstrates a use case where we want to raise an error when an unknown JSON key is encountered in the load (de-serialization) process.
import logging
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from dataclass_wizard.errors import UnknownJSONKey
# Sets up application logging if we haven't already done so
logging.basicConfig(level='DEBUG')
@dataclass
class Container(JSONWizard):
class _(JSONWizard.Meta):
# True to enable Debug mode for additional (more verbose) log output.
#
# Pass in a `str` to `int` to set the minimum log level:
# logging.getLogger('dataclass_wizard').setLevel('INFO')
debug = logging.INFO
# True to raise an class:`UnknownJSONKey` when an unmapped JSON key is
# encountered when `from_dict` or `from_json` is called. Note that by
# default, this is also recursively applied to any nested dataclasses.
on_unknown_key = 'RAISE'
element: 'MyElement'
@dataclass
class MyElement:
my_str: str
my_float: float
d = {
'element': {
'my_str': 'string',
'my_float': '1.23',
# Notice how this key is not mapped to a known dataclass field!
'my_bool': 'Testing'
}
}
# Try to de-serialize the dictionary object into a `MyClass` object.
try:
c = Container.from_dict(d)
except UnknownJSONKey as e:
print('Received error:', type(e).__name__)
print('Class:', e.class_name)
print('Unknown JSON key:', e.unknown_keys)
print('JSON object:', e.obj)
print('Known Fields:', e.fields)
else:
print('Successfully de-serialized the JSON object.')
print(repr(c))See the section on Handling Unknown JSON Keys for more info.
When calling from_dict or from_json, any unknown or extraneous JSON keys
that are not mapped to fields in the dataclass are typically ignored or raise an error.
However, you can capture these undefined keys in a catch-all field of type CatchAll,
allowing you to handle them as needed later.
For example, suppose you have the following dictionary:
dump_dict = {
"endpoint": "some_api_endpoint",
"data": {"foo": 1, "bar": "2"},
"undefined_field_name": [1, 2, 3]
}
You can save the undefined keys in a catch-all field and process them later.
Simply define a field of type CatchAll in your dataclass. This field will act
as a dictionary to store any unmapped keys and their values. If there are no
undefined keys, the field will default to an empty dictionary.
from typing import Any
from dataclass_wizard import DataclassWizard
from dataclass_wizard.models import CatchAll
class UnknownAPIDump(DataclassWizard):
endpoint: str
data: dict[str, Any]
unknown_things: CatchAll
dump_dict = {
"endpoint": "some_api_endpoint",
"data": {"foo": 1, "bar": "2"},
"undefined_field_name": [1, 2, 3]
}
dump = UnknownAPIDump.from_dict(dump_dict)
print(f'{dump!r}')
# > UnknownAPIDump(endpoint='some_api_endpoint', data={'foo': 1, 'bar': '2'},
# unknown_things={'undefined_field_name': [1, 2, 3]})
print(dump.to_dict())
# > {'endpoint': 'some_api_endpoint', 'data': {'foo': 1, 'bar': '2'}, 'undefined_field_name': [1, 2, 3]}Note
- When using a "catch-all" field, it is strongly recommended to define exactly one field of type
CatchAllin the dataclass. LetterCasetransformations do not apply to keys stored in theCatchAllfield; the keys remain as they are provided.- If you specify a default (or a default factory) for the
CatchAllfield, such asunknown_things: CatchAll = None, the default value will be used instead of an empty dictionary when no undefined parameters are present. - The
CatchAllfunctionality is guaranteed only when usingfrom_dictorfrom_json. Currently, unknown keyword arguments passed to__init__will not be written to aCatchAllfield.
Tip
As of v1.x, Dataclass Wizard now supports timezone-aware and UTC datetime
and time patterns, as well as multiple pattern strings (i.e. multiple custom formats) for greater
flexibility in pattern matching. These features are not available in the v0.* versions.
The new features include:
- Timezone-aware
datetimeandtimepatterns. - UTC
datetimeandtimepatterns. - Multiple custom formats for a single field, providing more control over pattern matching.
For more details and examples on how to use these new features, refer to the Documentation for Patterned Date and Time.
As of v0.20.0, date and time strings in custom formats can be de-serialized using the DatePattern,
TimePattern, and DateTimePattern type annotations, which represent patterned date, time, and
datetime objects, respectively.
Internally, these annotations use datetime.strptime with the specified format and the fromisoformat()
method for ISO-8601 formatted strings. All date and time values are still serialized to ISO format strings by
default. For more information, refer to the Patterned Date and Time section in the documentation.
Here is an example demonstrating how to use these annotations:
from dataclasses import dataclass
from datetime import time, datetime
from typing import Annotated
from dataclass_wizard import fromdict, asdict
from dataclass_wizard.patterns import DatePattern, TimePattern, Pattern
@dataclass
class MyClass:
# Custom format for date (Month-Year)
date_field: DatePattern['%m-%Y']
# Custom format for datetime (Month/Day/Year Hour.Minute.Second)
dt_field: Annotated[datetime, Pattern('%m/%d/%y %H.%M.%S')]
# Custom format for time (Hour:Minute)
time_field1: TimePattern['%H:%M']
# Custom format for a list of times (12-hour format with AM/PM)
time_field2: Annotated[list[time], Pattern('%I:%M %p')]
data = {'date_field': '12-2022',
'time_field1': '15:20',
'dt_field': '1/02/23 02.03.52',
'time_field2': ['1:20 PM', '12:30 am']}
class_obj = fromdict(MyClass, data)
# All annotated fields de-serialize to date, time, or datetime objects, as shown.
print(class_obj)
# MyClass(date_field=datetime.date(2022, 12, 1), dt_field=datetime.datetime(2023, 1, 2, 2, 3, 52),
# time_field1=datetime.time(15, 20), time_field2=[datetime.time(13, 20), datetime.time(0, 30)])
# All date/time fields are serialized as ISO-8601 format strings by default.
print(asdict(class_obj))
# {'dateField': '2022-12-01', 'dtField': '2023-01-02T02:03:52',
# 'timeField1': '15:20:00', 'timeField2': ['13:20:00', '00:30:00']}
# The patterned date/times can be de-serialized back after serialization, which will be faster than
# re-parsing the custom patterns!
assert class_obj == fromdict(MyClass, asdict(class_obj))Prior to version 0.27.0, dataclasses with cyclic references or self-referential structures were not supported. This limitation is shown in the following toy example:
from dataclasses import dataclass
@dataclass
class A:
a: 'A | None' = None
a = A(a=A(a=A(a=A())))This was a longstanding issue, but in v1.x, Dataclass Wizard now supports
recursive dataclasses OOTB, including cyclic references.
The example below demonstrates recursive
dataclasses with cyclic dependencies, following the pattern A -> B -> A -> B.
For more details, see the Cyclic or "Recursive" Dataclasses section in the documentation.
from __future__ import annotations # This can be removed in Python 3.10+
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class A(JSONWizard):
b: 'B | None' = None
@dataclass
class B:
a: A | None = None
# Confirm that `from_dict` with a recursive, self-referential
# input `dict` works as expected.
a = A.from_dict({'b': {'a': {'b': {'a': None}}}})
assert a == A(b=B(a=A(b=B())))Starting with version 1.x, recursive types are supported out of the box (OOTB),
removing the need for any Meta settings.
This makes working with recursive dataclasses even easier and more streamlined. In addition, recursive types are now supported for the following Python type constructs:
- NamedTuple
- TypedDict
- Union
- Literal
- Nested dataclasses
- Type aliases (introduced in Python 3.12+)
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.
from dataclasses import dataclass
from dataclass_wizard import DataclassWizard
# 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'
# For Python 3.10 and above, use this simpler approach:
# JSON = str | int | float | bool | dict[str, 'JSON'] | list['JSON'] | None
# For Python 3.12+, you can use the `type` statement:
# type JSON = str | int | float | bool | dict[str, JSON] | list[JSON] | None
class MyTestClass(DataclassWizard):
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.
from dataclasses import dataclass, field
from dataclass_wizard import JSONWizard
@dataclass
class A(JSONWizard):
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.
For more information, see:
These examples illustrate the power of recursive types in simplifying complex data structures while leveraging the functionality of dataclass-wizard.
The dataclass-wizard library fully supports declaring dataclass models in
Union types, such as list[Wizard | Archer | Barbarian].
Starting from v0.19.0, the library introduces two key features:
- Auto-generated tags for dataclass models (based on class names).
- A customizable tag key (default: __tag__) that identifies the model in JSON.
These options are controlled by the auto_assign_tags and tag_key attributes in the Meta config.
For example, if a JSON object looks like {"type": "A", ...}, you can set tag_key = "type" to automatically deserialize it into the appropriate class, like A.
Let's start out with an example, which aims to demonstrate the simplest usage of
dataclasses in Union types. For more info, check out the
Dataclasses in Union Types section in the docs.
from __future__ import annotations
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass
class Container(JSONWizard):
class Meta(JSONWizard.Meta):
tag_key = 'type'
auto_assign_tags = True
objects: list[A | B | C]
@dataclass
class A:
my_int: int
my_bool: bool = False
@dataclass
class B:
my_int: int
my_bool: bool = True
@dataclass
class C:
my_str: str
data = {
'objects': [
{'type': 'A', 'my_int': 42},
{'type': 'C', 'my_str': 'hello world'},
{'type': 'B', 'my_int': 123},
{'type': 'A', 'my_int': 321, 'myBool': True}
]
}
c = Container.from_dict(data)
print(repr(c))
# Output:
# Container(objects=[A(my_int=42, my_bool=False),
# C(my_str='hello world'),
# B(my_int=123, my_bool=True),
# A(my_int=321, my_bool=True)])
print(c.to_dict())
# True
assert c == c.from_json(c.to_json())What about untagged dataclasses in Union types or | syntax? With the major release V1, dataclass-wizard supercharges Union parsing, making it intuitive and flexible, even without tags.
This is especially useful for collections like list[Wizard] or when tags (discriminators) are not feasible.
from __future__ import annotations # Remove in Python 3.10+
from dataclasses import dataclass
from typing import Literal
from dataclass_wizard import DataclassWizard
class MyClass(DataclassWizard):
class _(DataclassWizard.Meta):
unsafe_parse_dataclass_in_union = True
literal_or_float: Literal['Auto'] | float
entry: int | MoreDetails
collection: list[MoreDetails | int]
@dataclass
class MoreDetails:
arg: str
# OK: Union types work seamlessly
c = MyClass.from_dict({
"literal_or_float": 1.23,
"entry": 123,
"collection": [{"arg": "test"}]
})
print(repr(c))
# > MyClass(literal_or_float=1.23, entry=123, collection=[MoreDetails(arg='test')])
# OK: Handles primitive and dataclass parsing
c = MyClass.from_dict({
"literal_or_float": "Auto",
"entry": {"arg": "example"},
"collection": [123]
})
print(repr(c))
# > MyClass(literal_or_float='Auto', entry=MoreDetails(arg='example'), collection=[123])Added in v0.30.0
Dataclass Wizard introduces conditional skipping to omit fields during JSON serialization based on user-defined conditions. This feature works seamlessly with:
- Global rules via
Metasettings. - Per-field controls using
SkipIf()annotations. - Field wrappers for maximum flexibility.
- Globally Skip Fields Matching a Condition
Define a global skip rule using
Meta.skip_if:from dataclass_wizard import DataclassWizard from dataclass_wizard.conditions import IS_NOT class Example(DataclassWizard): class _(DataclassWizard.Meta): skip_if = IS_NOT(True) # Skip fields if the value is not `True` my_bool: bool my_str: 'str | None' print(Example(my_bool=True, my_str=None).to_dict()) # Output: {'my_bool': True}
- Skip Defaults Based on a Condition
Skip fields with default values matching a specific condition using
Meta.skip_defaults_if:from __future__ import annotations # Can remove in PY 3.10+ from dataclass_wizard import DataclassWizard from dataclass_wizard.conditions import IS class Example(DataclassWizard): class _(DataclassWizard.Meta): skip_defaults_if = IS(None) # Skip default `None` values. str_with_no_default: str | None my_str: str | None = None my_bool: bool = False print(Example(str_with_no_default=None, my_str=None).to_dict()) # > {'str_with_no_default': None, 'my_bool': False}Note
Setting
skip_defaults_ifalso enablesskip_defaults=Trueautomatically.
- Per-Field Conditional Skipping
Apply skip rules to specific fields with annotations or
skip_if_field:from __future__ import annotations # can be removed in Python 3.10+ from dataclasses import dataclass from typing import Annotated from dataclass_wizard import JSONWizard, skip_if_field from dataclass_wizard.conditions import SkipIfNone, EQ @dataclass class Example(JSONWizard): my_str: Annotated[str | None, SkipIfNone] # Skip if `None`. other_str: str | None = skip_if_field(EQ(''), default=None) # Skip if empty. print(Example(my_str=None, other_str='').to_dict()) # Output: {}
Skip Fields Based on Truthy or Falsy Values
Use the
IS_TRUTHYandIS_FALSYhelpers to conditionally skip fields based on their truthiness:from dataclasses import dataclass, field from dataclass_wizard import JSONWizard from dataclass_wizard.conditions import IS_FALSY @dataclass class ExampleWithFalsy(JSONWizard): class _(JSONWizard.Meta): skip_if = IS_FALSY() # Skip fields if they evaluate as "falsy". my_bool: bool my_list: list = field(default_factory=list) my_none: None = None print(ExampleWithFalsy(my_bool=False, my_list=[], my_none=None).to_dict()) # > {}
Note
Special Cases
- SkipIfNone: Alias for
SkipIf(IS(None)), skips fields with a value ofNone. - Condition Helpers:
IS,IS_NOT: Identity checks.EQ,NE,LT,LE,GT,GE: Comparison operators.IS_TRUTHY,IS_FALSY: Skip fields based on truthy or falsy values.
Combine these helpers for flexible serialization rules!
The following parameters can be used to fine-tune and control how the serialization of a
dataclass instance to a Python dict object or JSON string is handled.
A common use case is skipping fields with default values - based on the default
or default_factory argument to dataclasses.field - in the serialization
process.
The attribute skip_defaults in the inner Meta class can be enabled, to exclude
such field values from serialization.The to_dict method (or the asdict helper
function) can also be passed an skip_defaults argument, which should have the same
result. An example of both these approaches is shown below.
from collections import defaultdict
from dataclasses import field, dataclass
from dataclass_wizard import JSONWizard
@dataclass
class MyClass(JSONWizard):
class _(JSONWizard.Meta):
skip_defaults = True
my_str: str
other_str: str = 'any value'
optional_str: 'str | None' = None
my_list: list[str] = field(default_factory=list)
my_dict: defaultdict[str, list[float]] = field(
default_factory=lambda: defaultdict(list))
print('-- Load (Deserialize)')
c = MyClass('abc')
print(f'Instance: {c!r}')
print('-- Dump (Serialize)')
string = c.to_json()
print(string)
assert string == '{"my_str": "abc"}'
print('-- Dump (with `skip_defaults=False`)')
print(c.to_dict(skip_defaults=False))You can also exclude specific dataclass fields (and their values) from the serialization process. There are two approaches that can be used for this purpose:
- The argument
skip=Truecan be passed in to theAliashelper function. Note that this is a more permanent option, as opposed to the one below. - The
to_dictmethod (or theasdicthelper function ) can be passed anexcludeargument, containing a list of one or more dataclass field names to exclude from the serialization process.
Additionally, here is an example to demonstrate usage of both these approaches:
from typing import Annotated
from dataclass_wizard import DataclassWizard, Alias
class MyClass(DataclassWizard):
my_str: str
my_int: int
other_str: Annotated[str, Alias('AnotherStr', skip=True)]
my_bool: bool = Alias('TestBool', skip=True)
data = {'my_str': 'my string',
'my_int': 1,
'AnotherStr': 'testing 123',
'TestBool': True}
print('-- From Dict')
c = MyClass.from_dict(data)
print(f'Instance: {c!r}')
# dynamically exclude the `my_int` field from serialization
additional_exclude = ('my_int',)
print('-- To Dict')
out_dict = c.to_dict(exclude=additional_exclude)
print(out_dict)
assert out_dict == {'my_str': 'my string'}Easily map environment variables to Python dataclasses with EnvWizard:
import os
from dataclass_wizard import EnvWizard
# Set up environment variables
os.environ.update({
'APP_NAME': 'Env Wizard',
'MAX_CONNECTIONS': '10',
'DEBUG_MODE': 'true'
})
# Define dataclass using EnvWizard
class AppConfig(EnvWizard):
app_name: str
max_connections: int
debug_mode: bool
# Load config from environment variables
config = AppConfig()
print(config.app_name) #> Env Wizard
print(config.debug_mode) #> True
assert config.max_connections == 10
# Override with keyword arguments
config = AppConfig(app_name='Dataclass Wizard Rocks!', debug_mode='false')
print(config.app_name) #> Dataclass Wizard Rocks!
assert config.debug_mode is FalseNote
EnvWizard simplifies environment variable mapping with type validation, .env file support, and secret file handling (file names become keys).
Key Features:
- Auto Parsing: Supports complex types and nested structures.
- Configurable: Customize variable names, prefixes, and dotenv files.
- Validation: Errors for missing or malformed variables.
EnvWizard supports dynamic prefix application, ideal for customizable environments:
import os
from dataclass_wizard import EnvWizard, Env
# Define dataclass with custom prefix support
class AppConfig(EnvWizard):
class _(EnvWizard.Meta):
env_prefix = 'APP_' # Default prefix for env vars
name: str = Env('A_NAME') # Looks for `A_NAME` (explicit override)
debug: bool
# Set environment variables
os.environ['A_NAME'] = 'Test!'
os.environ['CUSTOM_DEBUG'] = 'yes'
# Apply a dynamic prefix at runtime
config = AppConfig(__env__={'prefix': 'CUSTOM_'}) # Looks for `A_NAME` and `CUSTOM_DEBUG`
print(config)
# > AppConfig(name='Test!', debug=True)The Python dataclasses library has some key limitations
with how it currently handles properties and default values.
The dataclass-wizard package natively provides support for using
field properties with default values in dataclasses. The main use case
here is to assign an initial value to the field property, if one is not
explicitly passed in via the constructor method.
To use it, simply import
the property_wizard helper function, and add it as a metaclass on
any dataclass where you would benefit from using field properties with
default values. The metaclass also pairs well with the JSONWizard
mixin class.
For more examples and important how-to's on properties with default values, refer to the Using Field Properties section in the documentation.
Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change.
Check out the Contributing section in the docs for more info.
All feature ideas or suggestions for future consideration, have been currently added as milestones in the project's GitHub repo.
This package was created with Cookiecutter and the rnag/cookiecutter-pypackage project template.
