Generate Python dataclasses from JSON
Convert JSON into Python dataclasses with modern type hints, snake_case attribute names and optional fields defaulting to None.
- Runs in your browser
- Files are never uploaded
- Free, no account
The name given to the outermost type.
Your JSON never leaves this tab — check it yourself—
Starting the network probe…
The counter above wraps fetch, XMLHttpRequest and navigator.sendBeacon in this tab and counts every call. It does not block anything — it just reports.
Two ways to verify independently
- Turn off Wi-Fi or enable airplane mode, then use the tool. It keeps working, because the page is already fully loaded.
- Open your browser devtools, switch to the Network tab, clear it, and run the tool. No new row appears.
Why this matters
In 2025 researchers found that popular online formatters had retained tens of thousands of user-submitted snippets — JSONFormatter.org and CodeBeautify together exposed roughly 80,000 saved documents, many containing live cloud credentials, private keys and internal database connection strings, because “format this” quietly meant “upload this”. A separate incident exposed roughly 3.5 million user PDFs on a document-conversion service. The only structural fix is not sending the data in the first place: this site is served under a Content-Security-Policy whose connect-src contains no third-party origin, so a request off this device cannot be made even by accident.
What you get
One @dataclass per distinct nested object, emitted child-first so the file reads
top-down, with modern type hints: built-in generics (list[str], not
List[str]) and the X | None union syntax. The file opens with
from __future__ import annotations, which makes all annotations lazy and means
forward references resolve regardless of class order.
Target is Python 3.10 or later. For 3.8 and 3.9, swap the built-in generics for their typing equivalents and X | None for Optional[X].
Field names
JSON keys are converted to snake_case, which is what PEP 8 asks for and what the rest of your code will expect. Two things happen automatically:
- A name that collides with a Python keyword gets a trailing underscore:
classbecomesclass_,frombecomesfrom_. - Whenever the attribute name differs from the JSON key, the original key is written in a comment on the same line, so you can see what to map.
Required fields come first — and why they have to
Python raises TypeError: non-default argument follows default argument at class
definition time if a field without a default appears after one with a default. Optional fields are
therefore emitted last, with = None. If you reorder them by hand, keep that rule or
the module will not import.
One related trap the generator avoids by never emitting one: a mutable default. tags: list[str] = []
is an error in a dataclass, because the same list would be shared by every instance. If you add a
default for a list or dict, use field(default_factory=list).
Dataclasses do not deserialise nested JSON
This surprises people, so it is worth stating clearly. Order(**data) works for a flat
object. For a nested one, order.customer will be a plain dict, not a
Customer — the type hint is not enforced and nothing constructs the child. Three ways
to fix it:
| Approach | Cost | Best for |
|---|---|---|
Write a from_dict classmethod per class | Manual, explicit, no dependency | A handful of classes |
dacite.from_dict(Order, data) | One small dependency, keeps the dataclasses | Most cases |
Switch to Pydantic BaseModel | Bigger dependency, but validation and coercion included | API boundaries |
If you are already using Pydantic, converting is close to mechanical: replace
@dataclass with a class X(BaseModel) declaration and delete the
decorator. The field annotations carry over unchanged.
Types that need a second look
- Timestamps arrive as
str. Convert withdatetime.fromisoformat, which since 3.11 handles the trailingZ. - Money arrives as
float. Change it toDecimaland make sure the API sends it as a string, or you inherit binary-float rounding. - A field that is always one of a few strings should become an
Enumor aLiteral[...]. - A dict keyed by ID generates one attribute per key in your sample. Replace the whole class with
dict[str, Thing].
Questions
Which Python version does the output target?
Python 3.10 and later. It uses built-in generics (`list[str]` rather than `List[str]`) and the `X | None` union syntax, and includes `from __future__ import annotations` so the forward references between classes resolve regardless of definition order.
What happens to a key that is not a valid identifier?
It is converted to snake_case, and a trailing underscore is added if the result collides with a Python keyword (`class` becomes `class_`). The original JSON key is preserved in a comment on the same line so you can wire up an alias.
Will these classes deserialise JSON automatically?
No. `SomeClass(**data)` works for a flat object, but nested dataclasses need construction by hand or a library such as dacite, pydantic or mashumaro. The generated file gives you the shape; the parsing strategy is yours.
Why do optional fields come last?
Because a dataclass field with a default cannot precede one without — Python raises a TypeError at class definition time. Required fields are emitted first, then optionals with `= None`.