dataclasses-json review
dataclasses-json 0.6.7 attaches JSON and dictionary conversion methods to standard Python dataclasses, either with `@dataclass_json` or `DataClassJsonMixin`. It understands nested dataclasses, abstract collections, UUIDs, decimals, datetimes, renamed keys, case conversion, and field-specific codecs. The ordinary `from_json()` and `from_dict()` routes construct objects without checking values against annotations. Validation is a separate Marshmallow schema call. The 0.6.7 release expanded abstract collection handling and added warnings when runtime type resolution is unreliable.
dataclasses-json 0.6.7 installed in 0.2 seconds and used 2 MB in our sandbox, but its normal decoder accepts values that violate annotations. It is reasonable glue for existing dataclasses on Marshmallow 3; choose a validation-first model library for new untrusted inputs.
We installed it
| Install | ✓ · 0.2s | 6 packages on disk · 2 MB |
| Import | ✓ | import dataclasses_json in 0.31s · pure Python · py.typed · requires Python >=3.7,<4.0 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does dataclasses-json install cleanly?
Yes. In a fresh container with an empty cache, pip install dataclasses-json finished in 0.2s, leaving 6 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.
What does dataclasses-json need to run?
Python >=3.7,<4.0, and nothing compiled: it is pure Python. In our run import dataclasses_json succeeded in 0.31s, and the package ships py.typed for type checkers.
dataclasses-json or pydantic: which should you use?
pydantic: Choose it when the everyday parse path must validate untrusted values and emit JSON Schema. dataclasses-json 0.6.7 installed in 0.2 seconds and used 2 MB in our sandbox, but its normal decoder accepts values that violate annotations.
When should you not use dataclasses-json?
Every decode must validate annotations. The README shows from_json() accepting 42 for a str; only .schema().loads() raises ValidationError for that payload.
Use it if
- Existing models are stdlib dataclasses and replacing them with a validation framework would create needless migration work.
- The wire format uses camelCase or fixed external names that should be declared in dataclass metadata.
- Each class needs an explicit policy to reject, discard, or retain unknown keys in a `CatchAll` field.
- Only selected ingress points need Marshmallow validation; trusted internal conversions can use the shorter direct methods.
- Every decode must validate annotations. The README shows `from_json()` accepting `42` for a `str`; only `.schema().loads()` raises `ValidationError` for that payload.
- Your dependency set requires Marshmallow 4. The 0.6.7 metadata pins `marshmallow>=3.18,<4.0.0`.
- The project relies heavily on `from __future__ import annotations`. Its README warns that deferred annotations can defeat runtime inspection around recursive and generic types.
- Datetimes must round-trip without custom metadata. Defaults encode numeric timestamps, read naive values in the host timezone, and decode aware objects.
- Release recency is a purchasing criterion. PyPI's newest file is from June 9, 2024, the version remains below 1.0, and GitHub lists 165 open issues and pull requests.
Setup reality
Our uncached Python 3.12 install of dataclasses-json 0.6.7 finished in 0.2 seconds, left 6 packages totaling 2 MB, and produced 0 pip-audit findings. The distribution is pure Python with 2 direct dependencies, declares Python >=3.7,<4.0, ships py.typed, and uses MIT. import dataclasses_json succeeded in 0.31 seconds.
Decorator order is fixed: put @dataclass_json above @dataclass. The mixin is easier for static analysis because its methods are visible in the base class. Marshmallow stays below version 4 through the package constraint. .schema() builds a schema object, so create it once for a hot path. Direct from_json() remains quicker to write but does not enforce annotated types.
Defaults fill absent keys. Avoid infer_missing unless assigning None to a non-Optional field is acceptable. Without an Undefined policy, from_dict() drops extra keys while the schema loader rejects them. Undefined.INCLUDE needs exactly one CatchAll member. Class-wide letter-case conversion also assumes the Python attributes begin in snake_case.
The built-in datetime path emits a number, applies the machine timezone to naive input, and returns an aware value. For ISO text, define encoder, decoder, and mm_field together so direct and schema conversions agree. Quote recursive forward references. Process-wide codec registrations affect every later class that sees that type, so install them during startup rather than per request.
Patterns
Decorate a dataclass for JSON conversion serialize-dataclass
from dataclasses import dataclass
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Person:
name: str
age: int = 0
person = Person(name='Ada', age=36)
payload = person.to_json()
copy = Person.from_json(payload)`@dataclass_json` must be the outer decorator. In 0.6.7, `from_json()` constructs the object without enforcing its annotations.
Make serializer methods visible to type checkers support-static-analysis
from dataclasses import dataclass
from dataclasses_json import DataClassJsonMixin
@dataclass
class Person(DataClassJsonMixin):
name: str
payload = Person('Ada').to_json()
result = Person.from_json(payload)`DataClassJsonMixin` provides the same runtime methods while making them explicit in the class hierarchy for static analysis.
Convert class keys to camelCase convert-letter-case
from dataclasses import dataclass
from dataclasses_json import LetterCase, dataclass_json
@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Account:
given_name: str
account_id: int
text = Account('Ada', 7).to_json()
account = Account.from_json(text)Class-level case conversion assumes the attributes already follow snake_case; other starting forms have undefined mappings.
Give one attribute a fixed wire key rename-field
from dataclasses import dataclass, field
from dataclasses_json import config, dataclass_json
@dataclass_json
@dataclass
class User:
user_id: str = field(metadata=config(field_name='id'))
assert User('u-1').to_dict() == {'id': 'u-1'}`field_name` handles a single contract-specific key. Use `LetterCase` only when the entire model follows one rule.
Leave an internal field out of JSON exclude-output-field
from dataclasses import dataclass, field
from dataclasses_json import Exclude, config, dataclass_json
@dataclass_json
@dataclass
class Session:
user_id: str
internal_note: str = field(
default='',
metadata=config(exclude=Exclude.ALWAYS),
)This exclusion affects output. A required excluded field prevents the class from reading its own JSON unless it has a default.
Decode an array with one cached schema decode-json-array
from dataclasses import dataclass
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Item:
sku: str
item_schema = Item.schema()
items = item_schema.loads('[{"sku": "A1"}]', many=True)
text = item_schema.dumps(items, many=True)Top-level arrays use Marshmallow's `many=True`. Build `Item.schema()` once instead of recreating it for every batch.
Reject a wrong field type at ingress validate-input
from dataclasses import dataclass
from dataclasses_json import dataclass_json
from marshmallow import ValidationError
@dataclass_json
@dataclass
class Person:
name: str
try:
person = Person.schema().loads('{"name": 42}')
except ValidationError as error:
print(error.messages)The direct decoder accepts this integer. Route untrusted payloads through `.schema().loads()` when a mismatch must fail.
Retain unknown keys in CatchAll handle-unknown-keys
from dataclasses import dataclass
from dataclasses_json import CatchAll, Undefined, dataclass_json
@dataclass_json(undefined=Undefined.INCLUDE)
@dataclass
class Event:
kind: str
extras: CatchAll
event = Event.from_dict({'kind': 'created', 'source': 'api'})`Undefined.INCLUDE` requires one `CatchAll` member. Use `RAISE` for a closed schema or `EXCLUDE` when forwards-compatible additions should disappear.
Apply defaults to absent JSON keys apply-defaults
from dataclasses import dataclass, field
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Search:
query: str
tags: list[str] = field(default_factory=list)
search = Search.from_json('{"query": "paper"}')A declared default preserves the type contract. `infer_missing` may insert `None` into a field that was never annotated Optional.
Make datetime JSON use ISO strings encode-iso-datetime
from dataclasses import dataclass, field
from datetime import datetime
from dataclasses_json import config, dataclass_json
from marshmallow import fields
@dataclass_json
@dataclass
class Event:
created_at: datetime = field(metadata=config(
encoder=datetime.isoformat,
decoder=datetime.fromisoformat,
mm_field=fields.DateTime(format='iso'),
))All 3 hooks keep the direct and schema APIs consistent. Without them, a naive datetime becomes a timestamp and returns timezone-aware.
Attach a Decimal codec to one field add-custom-codec
from dataclasses import dataclass, field
from decimal import Decimal
from dataclasses_json import config, dataclass_json
@dataclass_json
@dataclass
class Price:
amount: Decimal = field(metadata=config(
encoder=str,
decoder=Decimal,
))
price = Price.from_json('{"amount": "12.50"}')These callables cover direct conversion. Add `mm_field` when the same class also loads data through its Marshmallow schema.
Decode a self-referential dataclass model-recursive-tree
from dataclasses import dataclass
from typing import Optional
from dataclasses_json import dataclass_json
@dataclass_json
@dataclass
class Node:
value: str
child: Optional['Node'] = None
root = Node.from_dict({'value': 'a', 'child': {'value': 'b'}})Quote the recursive name. The project warns that blanket deferred annotations can confuse the runtime type lookup used by 0.6.7.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pydantic | PyPI | Choose it when the everyday parse path must validate untrusted values and emit JSON Schema. |
| marshmallow | PyPI | Choose it for explicit schema classes or a codebase that has already adopted Marshmallow 4. |
| mashumaro | PyPI | Choose it for generated dataclass serializers and formats beyond JSON. |
| cattrs | PyPI | Choose it when attrs and dataclass models must remain undecorated and one converter should own the rules. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

