dacite
dacite turns dictionaries into typed Python dataclass instances. Its single main function walks nested dataclasses, collections, optionals, unions, generics, and forward references, then constructs the target objects while checking annotated types. A Config object can cast enums, run type-specific conversion hooks, translate field names, reject extra keys, or require an unambiguous union match. It is a construction helper, not a serializer, schema generator, or full input-validation framework.
Excellent at one narrow job: constructing nested dataclasses from already trusted dictionaries. If the source is external or you also need schemas and serialization, choose a validation or conversion framework that owns the full boundary.
Use it if
- You already model internal data with standard-library dataclasses and need to hydrate nested objects from JSON-decoded dictionaries
- You want a narrow dependency-free mapper rather than adopting a second model system such as Pydantic
- Your conversions can be expressed with type hooks, simple casts, or one global key-conversion function
- You want clear exceptions with nested field paths for missing values, wrong types, unexpected keys, and ambiguous unions
- You need untrusted-data validation: the README explicitly says dacite is not a data-validation library and recommends validating before construction
- You need serialization back to JSON, generated schemas, field aliases, validators, constrained values, or settings loading: dacite only builds dataclasses from dictionaries
- Input unions overlap heavily: default matching picks the first working member, while strict_unions_match rejects ambiguity instead of using discriminators
- You want ongoing feature velocity or rapid Python-version policy updates: version 1.9.2 was released in February 2025 and the repository's latest push was March 2025
- You need per-field alias rules: Config exposes one convert_key function that maps each dataclass field name to an input key, not declarative aliases or versioned migration logic
Setup reality
`pip install dacite` adds no runtime dependencies and version 1.9.2 supports Python 3.7 or newer. The basic call is genuinely small, but production behavior depends on Config choices. Extra input keys are ignored by default, so API payload typos can disappear unless `strict=True`. Type checking is on by default, yet that is still structural construction rather than business validation: no range, length, email, cross-field, or custom field constraint runs unless you perform it elsewhere. Optional fields omitted from input become None, while required fields raise MissingValueError and dataclass defaults remain available. Unions are tried in declaration order, which makes overlapping dataclasses order-dependent; turn on `strict_unions_match` if ambiguity should fail. Type hooks run by target type and can affect every matching field, while `cast` calls the declared type constructor, so both can quietly normalize bad source data if used broadly. The `convert_key` callable receives dataclass field names and must return the corresponding input key, a direction that is easy to reverse accidentally. Forward references may need an explicit name-to-type mapping. Dacite caches resolved type information with a default maximum size of 2,048; applications generating many dataclass types can change or clear that global cache. Keep raw payload validation, error translation, and serialization outside this package.
Patterns
Build a dataclass from a dictionaryconstruct-dataclass
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class User:
name: str
age: int
user = from_dict(User, {'name': 'Ada', 'age': 37})This checks annotated runtime types but does not validate business rules such as nonnegative age or nonempty name.
Construct nested dataclassesconstruct-nested-models
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class Address:
city: str
@dataclass
class User:
name: str
address: Address
user = from_dict(User, {'name': 'Ada', 'address': {'city': 'London'}})Nested dictionaries are recursively converted only where the target annotation is a dataclass type.
Convert lists of nested dataclassesconstruct-collections
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class Item:
sku: str
quantity: int
@dataclass
class Order:
items: list[Item]
order = from_dict(Order, {'items': [{'sku': 'A1', 'quantity': 2}]})Collection element annotations drive recursive conversion; an untyped list cannot tell dacite which object to create.
Let omitted optional fields become Noneuse-optional-fields
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class Profile:
handle: str
bio: str | None
profile = from_dict(Profile, {'handle': 'ada'})
assert profile.bio is NoneA missing non-optional field without a dataclass default raises MissingValueError.
Convert ISO timestamps with a type hookparse-datetime-hook
from dataclasses import dataclass
from datetime import datetime
from dacite import Config, from_dict
@dataclass
class Event:
occurred_at: datetime
config = Config(type_hooks={datetime: datetime.fromisoformat})
event = from_dict(Event, {'occurred_at': '2026-08-08T12:30:00+00:00'}, config)A type hook applies to every field with that target type. Catch ValueError from the hook separately from dacite's own errors.
Cast strings into enum memberscast-enum-values
from dataclasses import dataclass
from enum import Enum
from dacite import Config, from_dict
class State(Enum):
OPEN = 'open'
CLOSED = 'closed'
@dataclass
class Ticket:
state: State
ticket = from_dict(Ticket, {'state': 'open'}, Config(cast=[Enum]))Casting Enum as a base class affects every enum field. Invalid values raise from the enum constructor.
Fail on unexpected input keysreject-extra-keys
from dacite import Config, UnexpectedDataError, from_dict
try:
user = from_dict(User, payload, Config(strict=True))
except UnexpectedDataError as error:
raise ValueError(f'unexpected keys: {sorted(error.keys)}') from errorStrict mode is off by default, so misspelled or newly added payload keys are otherwise ignored.
Reject ambiguous union matchesrequire-unambiguous-union
from dataclasses import dataclass
from dacite import Config, from_dict
@dataclass
class Cat:
name: str
@dataclass
class Dog:
name: str
@dataclass
class Pet:
animal: Cat | Dog
pet = from_dict(Pet, {'animal': {'name': 'Milo'}}, Config(strict_unions_match=True))This example raises StrictUnionMatchError because both members fit. Dacite has no discriminator mechanism to choose one.
Map snake_case fields to camelCase keysconvert-input-keys
from dacite import Config, from_dict
def to_camel(field: str) -> str:
first, *rest = field.split('_')
return first + ''.join(part.title() for part in rest)
config = Config(convert_key=to_camel)
person = from_dict(Person, {'firstName': 'Ada', 'lastName': 'Lovelace'}, config)convert_key receives each dataclass field name and returns the input dictionary key, not the reverse.
Supply a forward-reference namespaceresolve-forward-reference
from dataclasses import dataclass
from dacite import Config, from_dict
@dataclass
class Node:
child: 'Leaf'
@dataclass
class Leaf:
value: str
node = from_dict(Node, {'child': {'value': 'x'}}, Config(forward_references={'Leaf': Leaf}))Unresolvable names raise ForwardReferenceError. Keep the mapping aligned when types move between modules.
Report the nested field that failedtranslate-field-errors
from dacite import DaciteFieldError, from_dict
try:
order = from_dict(Order, payload)
except DaciteFieldError as error:
raise ValueError(f'invalid payload at {error.field_path}: {error}') from errorDaciteFieldError covers field-aware failures, but UnexpectedDataError and ForwardReferenceError use different base paths and may need separate handling.
Control dacite's global type cachetune-type-cache
from dacite import clear_cache, get_cache_size, set_cache_size
print(get_cache_size()) # 2048
set_cache_size(4096)
try:
run_dynamic_imports()
finally:
clear_cache()Cache sizing is process-global. Only change it when profiling shows many dynamically generated dataclass types or repeated resolution work.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| cattrs | PyPI | You need both structuring and unstructuring, generated converters, and broader attrs plus dataclass support |
| mashumaro | PyPI | You want fast code-generated serialization and deserialization methods attached to dataclasses |
| pydantic | PyPI | You need validation, rich errors, aliases, JSON Schema, and serialization as part of the model layer |
| dataclasses-json | PyPI | You want JSON load and dump helpers directly on dataclasses and accept decorator-driven configuration |