mashumaro review
mashumaro 3.22 turns Python type annotations into generated encoders and decoders. Dataclass mixins add dictionary, JSON, YAML, TOML, and MessagePack methods; codecs handle standalone shapes such as list[Event] or a union. Configuration covers aliases, omitted values, tagged variants, custom serialization strategies, hooks, dialects, and unknown keys. It also builds JSON Schema from annotated models. Version 3.22 adds JSON Schema support for `Annotated[..., JSONSchema(...)]` plus contentEncoding, contentMediaType, and contentSchema. This is conversion rather than a user-input validation framework: an object can deserialize successfully and still violate a business rule.
mashumaro 3.22 installed in 0.4 seconds, occupied 1 MB across 2 packages, and imported in 0.20 seconds with 0 audit findings in our sandbox. It is a strong fit for fast typed serialization around dataclasses, but choose a validation-focused model library when rejected input needs precise client-facing errors.
We installed it
| Install | ✓ · 0.4s | 2 packages on disk · 1 MB |
| Import | ✓ | import mashumaro in 0.20s · pure Python · py.typed · requires Python >=3.10 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does mashumaro install cleanly?
Yes. In a fresh container with an empty cache, pip install mashumaro finished in 0.4s, leaving 2 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does mashumaro need to run?
Python >=3.10, and nothing compiled: it is pure Python. In our run import mashumaro succeeded in 0.20s, and the package ships py.typed for type checkers.
mashumaro or pydantic: which should you use?
pydantic: Use it when validation, coercion rules, and detailed error objects are part of the public input contract. mashumaro 3.22 installed in 0.4 seconds, occupied 1 MB across 2 packages, and imported in 0.20 seconds with 0 audit findings in our sandbox.
When should you not use mashumaro?
HTTP input needs coercion policies and structured validation errors. Pydantic is built around that boundary; mashumaro chiefly converts annotated values.
Use it if
- Your domain types are dataclasses and repeated serialization should use generated functions instead of runtime reflection.
- One model needs aliases, omitted defaults, format-specific dialects, or a tagged union without moving to a new model base class.
- A reusable codec must encode a typed collection or union that has no root dataclass.
- JSON Schema should be derived from the same annotations used by the Python serializer.
- HTTP input needs coercion policies and structured validation errors. Pydantic is built around that boundary; mashumaro chiefly converts annotated values.
- The application still supports Python 3.9. Release 3.22 requires Python >=3.10, and the project lists 3.20 as the final 3.9-compatible release.
- Generated methods make debugging unacceptable for the team. Unsupported annotations can fail during class creation, and tracebacks may enter generated converter code.
- YAML, TOML, MessagePack, or orjson must work from the base install. Each path needs its related optional package or extra.
- The wire contract is intentionally independent from Python dataclasses. A separately defined schema and explicit mapper will make that separation easier to review.
Setup reality
Our mashumaro 3.22 install completed in 0.4 seconds in a clean Python 3.12 Bookworm container. It left 2 packages totaling 1 MB, and import mashumaro worked in 0.20 seconds. pip-audit found 0 known vulnerabilities. The pure-Python distribution reports 6 direct dependencies, requires Python >=3.10, and includes py.typed. PyPI did not declare a license in the measured metadata, even though the repository README labels the project Apache 2.0.
For dataclasses, inherit DataClassDictMixin or a format-specific mixin. A codec is better for a root collection, union, or third-party generic. The generator follows annotations and config, so postponed annotations, generic specialization, discriminators, and custom strategies need fixtures using real payloads. Unknown input keys are accepted by default; set forbid_extra_keys when a misspelled configuration option should fail.
The default JSON path uses the standard library. orjson, YAML, TOML, and MessagePack each require their own extra dependency. Aliases can be accepted while decoding, but output uses them only when serialize_by_alias is enabled. omit_none and omit_default change the meaning of patch payloads because an absent field may differ from an explicit null or default.
Converter code is normally compiled as the class is created, so a bad annotation can break an import rather than the first request. lazy_compilation defers that cost and failure to first use; debug prints generated source for inspection. Construct codecs once and reuse them instead of regenerating functions inside a loop. Hooks and strategies run application code, so keep their side effects out of round-trip conversion.
Patterns
Convert a dataclass through a dictionary dict-round-trip
from dataclasses import dataclass
from datetime import datetime
from mashumaro import DataClassDictMixin
@dataclass
class Event(DataClassDictMixin):
name: str
starts_at: datetime
event = Event.from_dict({'name': 'deploy', 'starts_at': '2026-08-26T09:00:00'})
wire = event.to_dict()from_dict converts according to annotations; it does not decide whether the timestamp or event name is valid for your domain.
Add JSON methods to a dataclass json-round-trip
from dataclasses import dataclass
from mashumaro.mixins.json import DataClassJSONMixin
@dataclass
class User(DataClassJSONMixin):
id: int
email: str
user = User.from_json('{"id": 7, "email": "dev@example.com"}')
body = user.to_json()The standard JSON mixin returns text. orjson support is a separate extra and has a bytes-oriented method.
Decode a typed root collection reuse-codec
from mashumaro.codecs.json import JSONDecoder
decoder = JSONDecoder(list[dict[str, int]])
rows = decoder.decode('[{"count": 4}]')Build the codec once. Recreating it for each request repeats code-generation work.
Use a different wire key alias-field
from dataclasses import dataclass, field
from mashumaro import DataClassDictMixin, field_options
from mashumaro.config import BaseConfig
@dataclass
class Item(DataClassDictMixin):
item_id: int = field(metadata=field_options(alias='itemId'))
class Config(BaseConfig):
serialize_by_alias = Trueserialize_by_alias makes output use itemId. Test input by both alias and Python name if compatibility depends on both.
Reject misspelled configuration keys reject-unknown-keys
from mashumaro.config import BaseConfig
class Config(BaseConfig):
forbid_extra_keys = TrueExtra keys are accepted by default. Enable this on configuration models when ignoring a typo would be dangerous.
Leave nulls and defaults out of output omit-empty-values
from dataclasses import dataclass
from mashumaro import DataClassDictMixin
from mashumaro.config import BaseConfig
@dataclass
class Patch(DataClassDictMixin):
label: str | None = None
retries: int = 3
class Config(BaseConfig):
omit_none = True
omit_default = TrueAn omitted field and an explicit null can mean different operations in a patch API. Confirm the receiver's rule first.
Serialize Decimal as a string custom-decimal
from decimal import Decimal
from mashumaro.types import SerializationStrategy
class DecimalText(SerializationStrategy):
def serialize(self, value):
return format(value, 'f')
def deserialize(self, value):
return Decimal(value)Provide both directions for round trips, then register the strategy for Decimal in Config.serialization_strategy.
Select a subtype by discriminator tagged-union
from typing import Annotated
from mashumaro.types import Discriminator
Payload = Annotated[BaseMessage, Discriminator(field='kind', include_subtypes=True)]Every subtype needs a distinct kind value. Add tests for absent and unknown discriminator values.
Encode JSON bytes with orjson orjson-bytes
# pip install 'mashumaro[orjson]'
from mashumaro.mixins.orjson import DataClassORJSONMixin
@dataclass
class Tick(DataClassORJSONMixin):
symbol: str
raw = Tick('AAPL').to_jsonb()to_jsonb() returns bytes. Do not pass it to an interface that expects a Python string without decoding.
Attach content metadata to JSON Schema schema-metadata
from typing import Annotated
from mashumaro.jsonschema.annotations import JSONSchema
EncodedFile = Annotated[
str,
JSONSchema(contentEncoding='base64', contentMediaType='application/pdf'),
]Version 3.22 adds these JSON Schema content fields. They describe the string; they do not decode or inspect its bytes.
Compile converters on first use delay-compilation
from mashumaro.config import BaseConfig
class Config(BaseConfig):
lazy_compilation = TrueThis moves generation cost and unsupported-annotation errors from import time to the first conversion.
Print generated converter source inspect-generated-code
from mashumaro.config import BaseConfig
class Config(BaseConfig):
debug = TrueDebug output can expose model fields and is noisy. Keep it out of normal production logging.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pydantic | PyPI | Use it when validation, coercion rules, and detailed error objects are part of the public input contract. |
| cattrs | PyPI | Use it for attrs or dataclass structuring with dispatch hooks and no required serialization mixin. |
| dacite | PyPI | Use it when the whole requirement is turning dictionaries into dataclasses with a smaller API. |
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.

