What this tool does
How to use it
Paste JSON (or load the example) and read the Python dataclasses on the right. Each nested object becomes its own dataclass with type annotations; the output is Python 3.7+ compatible.
Input: {"id":42,"name":"devsmiths","createdAt":"2024-03-11T08:24:00Z","stars":1280,"public":true,"contributors":[{"login":"ada","commits":51,"admin":true},{"login":"linus","commits":33,"admin":false}],"homepage":null}
Output (Python):
from dataclasses import dataclass
from typing import Any, List, Optional
@dataclass
class Contributor:
login: str
commits: int
admin: bool
@dataclass
class Root:
id: int
name: str
created_at: str
stars: int
public: bool
contributors: List[Contributor]
homepage: Optional[Any]Limits and edge cases
- Output is
@dataclasswith type annotations, not PydanticBaseModelorTypedDict. Dataclasses are stdlib (Python 3.7+) and runtime-free; if you want validation, wrap the dataclass output with Pydantic via@dataclasses.dataclass+pydantic.dataclasses.dataclass, or rewrite by hand. - Field names are converted to
snake_casefrom camelCase JSON keys (createdAt→created_at). This breaksjson.loads()+**dictconstruction — use the generatedfrom_dictconverter orcattrsto handle the renaming. - Nullable fields use
Optional[T](which isUnion[T, None]), not the PEP 604T | Nonesyntax. Run the output through pyupgrade if you’re on Python 3.10+ and prefer the new syntax. - Reserved words and dunder names in JSON keys are escaped with a trailing underscore (
class_,type_), with the original spelling preserved in a__post_init__rename map. - Date / datetime strings stay as
str; the generator can’t infer ISO 8601 vs slug. Convert viadatetime.fromisoformat()in a post-init hook or withcattrshooks. - Single-sample inference limitation applies: keys absent from the sample don’t become
Optionalautomatically.
Frequently asked questions
- Why dataclasses and not Pydantic BaseModel?
- Dataclasses are stdlib (no runtime dep) and the output works in any Python 3.7+ project. Pydantic is great for runtime validation but adds a ~5 MB dependency tree and changes the semantics (Pydantic coerces types, dataclasses don't). To upgrade: wrap each `@dataclass` with `pydantic.dataclasses.dataclass` — same field syntax, Pydantic validation on construction.
- How do I parse a JSON string into the generated dataclasses?
- Use the from-dict converter the generator emits: `Root.from_dict(json.loads(payload))`. Pure stdlib `json.loads()` returns a `dict`, not a `Root`. For nested objects, the from-dict walks the tree. If you prefer cattrs (more configurable), the dataclass shapes are compatible — pass them to `cattrs.structure(payload_dict, Root)`.
- Why is my JSON key createdAt renamed to created_at?
- PEP 8 says Python variable names should be snake_case. The generator follows PEP 8 for the Python side, but JSON serialization needs to know the original key. The from-dict converter handles the rename; if you use a different deserializer (Pydantic, marshmallow), configure it with `populate_by_name = True` and `alias = 'createdAt'`, or convert the dict keys before passing in.
- Can I get PEP 604 union syntax (X | None) instead of Optional[X]?
- Not from the generator directly. Run the output through `pyupgrade --py310-plus` to convert `Optional[X]` to `X | None`. The semantics are identical; PEP 604 is just a cleaner syntax that requires Python 3.10+.
- What about Python keywords as JSON keys (class, def, type)?
- They're renamed with a trailing underscore (`class_`, `def_`, `type_`) and the original key is preserved in a `__post_init__` rename map for from-dict deserialization. The rename map is the cleanest way to handle this in Python — alternatives (using `__getattr__` for the original name) work but add runtime cost.
- Why doesn't the generator handle decimal.Decimal for money fields?
- Because the JSON spec has only one number type and no way to mark a value as 'this should stay decimal'. Convert by hand for money fields, or use a JSON parser that supports lossless number preservation (`simplejson` with `use_decimal=True`) and adjust the dataclass field type to `Decimal`. The generator stays float-only by default.
Content reviewed by ShiangYu Huang