JSON to Python — paste JSON, get @dataclass or Pydantic v2 models with type hints, Optional inference, nested classes, snake_case fields and aliases.
- Runs locally
- Category Developer & DevOps
- Best for Formatting, validating, shrinking, or inspecting code-adjacent text.
How types, Optional & aliases are inferred
Each JSON object becomes a named class — a @dataclass or a Pydantic v2 BaseModel. Integer numbers infer int; fractional numbers infer float; a number that is sometimes whole and sometimes fractional widens to float. Arrays of objects fold into ONE class — keys missing from some elements (or seen as null) become Optional[X] = None. A value that is only ever null types as Any. Keys that are not valid Python identifiers are converted to snake_case, and the original key is preserved via metadata (dataclass) or Field(alias=...) (Pydantic) so (de)serialisation round-trips. Leaf classes are emitted before their parents so the file imports top-to-bottom with no forward references.
What this tool does
Paste any JSON payload — a REST API response, a webhook body, a `config.json`, a fixture — and get back a set of fully type-annotated Python classes you can drop straight into a `.py` file. Flip one switch between a plain `@dataclass` and a Pydantic v2 `BaseModel`. Types are inferred, not flattened: an integer sample becomes `int`, a fractional one `float`, and a field that is whole in one sample and fractional in another widens to `float` so your model never rejects a valid row. `true`/`false` become `bool`, strings `str`, and a value that only ever appears as `null` becomes `Optional[Any]`. Arrays of objects fold into ONE class, so `[{"a":1},{"a":1,"b":2}]` gives you a single model with both fields — `b` flagged `Optional[int] = None` because it's missing from the first element, not two near-duplicate classes you reconcile by hand. Nested objects lift out as their own named classes (`User.address` → `UserAddress`) emitted leaf-first, so the file reads top-to-bottom and every reference resolves without a forward ref. JSON keys that aren't valid Python identifiers are cleaned to `snake_case` — `userId` → `user_id`, `is-active` → `is_active`, `2fa` → `field_2fa`, `class` → `class_` — and the original wire key is preserved with `Field(alias=...)` for Pydantic or `field(metadata=...)` for dataclass, so round-trip (de)serialisation stays correct. List and required-field ordering follows Python's rules (no non-default field after a default one), and mutable defaults use `field(default_factory=list)` to dodge the classic shared mutable bug. Rename the root class, share the input as a link, copy or download the `.py`. Everything runs in your browser — the JSON never touches a server.
Tool details
- Input
- Text + Numbers + Structured content
- The page exposes text boxes, numeric controls, file pickers, or structured inputs depending on the tool.
- Output
- Live result + Copy + Download
- The result area focuses on usable output, with copy, download, or preview actions when supported.
- Privacy
- Browser-side processing
- The main tool logic does not call an external API, so inputs normally stay in the current tab.
- Save / share
- Shareable URL state
- Key settings are encoded in the URL so another person can reopen the same setup.
- Performance budget
- Initial JS <= 25 KB
- No WASM budget is declared, keeping the tool quick to open on mobile.
- Best fit
- Developer & DevOps · Developer
- Category and role tags drive related tools, internal links, and quick fit checks.
How to use
-
1. Input
Paste or drop your content into the tool panel.
-
2. Process
Click the button. All processing is local in your browser.
-
3. Copy / Download
Copy the result or download to disk in one click.
How JSON to Python Dataclass fits into your work
Use it in the small gaps between coding, reviewing, debugging, and shipping.
Developer jobs
- Formatting, validating, shrinking, or inspecting code-adjacent text.
- Preparing snippets for documentation, tickets, commits, or handoff.
- Checking a small payload quickly without switching tools.
Developer checks
- Run irreversible transforms like minify or obfuscate on a copy.
- Keep secrets out of pasted snippets unless the tool explicitly stays local.
- Use your normal tests or linter before shipping transformed code.
Good next steps
These links move the current task into a more complete workflow.
- 1 JSON to Go Struct JSON to Go struct — paste JSON, get typed structs with json tags, exported fields, nested sub-structs, pointers for nullable, int64 and omitempty options. Open
- 2 JSON to TypeScript Interface JSON to TypeScript interface — paste JSON, get clean interfaces with union types from arrays, optional vs required detection, root name customizable. Open
- 3 JSON to PHP Array JSON to PHP array — paste JSON, get a clean associative or indexed array with short [] or array() syntax, 2/4 indent, trailing comma, and an optional <?php return …; wrapper. Open
Real-world use cases
Type a third-party API response without hand-writing classes
You're integrating a REST API and the user object has 30+ fields. Curl the endpoint once, paste the JSON, set the root name to `GitHubUser`, pick Pydantic v2, copy the output into `app/clients/github.py`. The nested `plan` object lifts out as `GitHubUserPlan` automatically, `html_url` becomes `html_url` with its alias, and now `GitHubUser.model_validate(resp.json())` gives you a typed object with editor autocomplete instead of a raw `dict` you index by string and pray.
Generate request/response models for a FastAPI endpoint
A teammate sends a sample JSON body for the `/orders` POST they're building. Paste it, name the root `CreateOrderRequest`, choose Pydantic v2 so FastAPI validates the body automatically. Paste the response sample too as `CreateOrderResponse`. Drop both into `schemas.py`, wire them into the route signature, and FastAPI's OpenAPI docs + 422 validation come for free — before either of you writes the handler.
Decide Optional vs required for a nullable webhook payload
A vendor's webhook sometimes includes `cancelled_at` and sometimes omits it. Log the first few payloads, paste them as a JSON array, and the Optional detection flags `cancelled_at` as `Optional[str] = None`. In your handler `if event.cancelled_at is not None:` cleanly distinguishes "cancelled" from "still active" — and Pydantic raises if a genuinely required field is missing, instead of a `KeyError` three calls deep.
Replace a dict[str, Any] blob with a real model
Old code does `data: dict = resp.json()` and reaches in with `data["user"]["profile"]["id"]`, exploding at runtime on a renamed key. Capture one representative response, paste it, generate the dataclass or model, replace the dict. The string indexing that bit you turns into attribute access (`data.user.profile.id`) with mypy / Pyright catching typos at edit time instead of in production.
Build typed config and fixtures for a Python service
Your service reads a `config.json`. Paste it, name the root `Config`, keep `@dataclass` for a zero-dependency loader. You get `Config`, `ConfigServer`, `ConfigServerTLS` nested classes with `int` ports and `bool` flags, and `list` fields already using `field(default_factory=list)`. Load it with `Config(**json.load(f))`, and a typo'd key fails at construction with a clear `TypeError` rather than silently passing through a dict.
Common pitfalls
Pasting a single object and expecting some fields to be Optional. A single object has no missing-key signal, so every field is required (no `= None`). Optional inference only kicks in across an array of objects where some elements carry a key and others don't, or when a value is literally `null`. To explore optionality from one sample, wrap it as `[{...}]`.
Forgetting that dataclass doesn't validate types at runtime. `@dataclass` only annotates; `User(id="not-an-int")` constructs happily and the wrong type slips through. If the JSON crosses a trust boundary (external API, user input), pick Pydantic v2 so `model_validate` actually coerces and rejects bad data. dataclass is for data you already trust.
Assuming the generated aliases work without populate_by_name. For Pydantic, `Field(alias="userId")` means `model_validate({"userId": 7})` works, but constructing by the Python name — `Account(user_id=7)` — raises unless you set `model_config = ConfigDict(populate_by_name=True)`. If you build instances in code (not just parse JSON), add that config or construct with the alias.
Editing a number's type to int when the JSON sometimes has decimals. If one sample is `1` and another is `1.5`, the field correctly widens to `float`. Manually narrowing it back to `int` will make Pydantic reject the `1.5` row (or dataclass store a float under an int annotation, fooling type checkers). Trust the widening — JSON `1` is a valid `float` in Python anyway.
Pasting JSON5 / JSON-with-comments / trailing commas. We use strict `JSON.parse`, so comments, unquoted keys, and trailing commas all error out with a line/column. Strip them first, or run the payload through a JSON formatter as a pre-step.
Privacy
Your JSON never leaves this browser tab. Parsing and type inference use the browser's built-in `JSON.parse`; there is no network call and no analytics on the textarea content. The Share link encodes your input and the root class name into the URL so a result is reproducible — that means YOU decide when to share. If the payload is sensitive (internal IDs, customer data, prod responses), copy the generated Python instead of sharing the link. Only your option choices (dataclass vs Pydantic, Optional handling, the future import) are stored in localStorage so your preferred style persists across visits.
FAQ
Tool combos
Folks in your role tend to reach for these alongside this tool.
- 555 Timer Calculator Astable f = 1.44/((R1+2R2)C) + monostable t = 1.1RC — pick R1, R2, C in Ω/kΩ and µF/nF, read frequency, duty cycle and pulse width — browser-only
- Add Line Numbers Number every line of pasted text — set start, step and separator, zero-pad to align, skip blanks, or strip numbers back off — browser-only
- AES Text Encryptor Encrypt & decrypt text with a password — AES-256-GCM + PBKDF2 via WebCrypto — 100% in your browser, nothing uploaded
- Affine Cipher Encoder & Decoder Encrypt and decrypt the ax+b affine cipher with live modular-inverse check, browser-only