JSON Formatter Pro

Code Generator

JSON to Python Dataclass Generator Pro

Turn a real API response into typed Python dataclasses — nested classes, Optional fields, and the original JSON keys preserved.

Nothing you paste leaves your browser

What is JSON to Python conversion?

JSON to Python conversion generates Python data models — @dataclass definitions — from a sample JSON document, so you can load that data into typed objects instead of passing around raw dictionaries. Dataclasses, introduced in Python 3.7 via PEP 557, are chosen over plain dictionaries or TypedDict because they give the model a real constructor, equality, a readable repr, and dataclasses.asdict for serialization. The generator infers a type hint for every field, distinguishing int from float, mapping text to str and true or false to bool, turning arrays into list[...] with the right element type, and creating a separate dataclass for each nested object. Keys missing from some records become Optional, and keys that are not valid Python identifiers are handled safely. JSON Formatter Pro generates the code entirely in your browser, so the JSON you paste is never uploaded. Paste a representative sample and copy ready-to-use, PEP 585-annotated dataclasses into your project.

Worked example: JSON → Python

JSON to Python conversion example Example: JSON input on the left is converted to Python output on the right. JSON { "id": 1, "name": "Alice", "roles": ["admin"]} convert Python @dataclassclass Root: id: int name: str roles: list[str]
Generates @dataclass models with correct type hints — int vs float, Optional for missing keys, and a class per nested object.

How JSON values map to Python types

JSON value Python type
string str
integer int
fractional number float
true / false bool
null Any
array list[T]
object a @dataclass
key absent in some records Optional[T]

Complete Guide to Generating Python Dataclasses from JSON

Almost every Python program that talks to an HTTP API starts the same way: `response.json()` hands back a `dict`, and from that moment the shape of the data lives only in your head. Each `payload["profile"]["timezone"]` is a string key your editor cannot autocomplete, mypy and pyright cannot check, and no refactoring tool can rename safely. Typing the response once, at the boundary, is what makes the rest of the module something a type checker can genuinely reason about.

A JSON to Python dataclass generator reads one sample JSON document and emits the Python classes that describe its shape. Every JSON object becomes a class decorated with `@dataclass`; nested objects become their own classes, referenced by name; and each key becomes an annotated attribute — `str`, `int`, `float`, `bool`, `list[...]`, or `Optional[...]` where a null or a missing key was observed. What comes back is an instantiable record with a generated constructor, `__repr__`, `__eq__` and `dataclasses.asdict`, rather than the untyped dictionary `json.loads` returns. The practical difference is tooling: a dictionary offers an editor nothing to autocomplete and a type checker nothing to verify, so a field renamed upstream surfaces as a runtime `KeyError` instead of a static error. Generating the classes from a response you actually received, rather than writing them by hand from documentation, keeps the model honest about what the endpoint really sends.

JSON to Python Dataclass Generator Pro does exactly that, in your browser, with nothing to install and no signup.

How to Convert JSON to Python Dataclasses Online

  1. Paste a Real API Response: Paste your JSON into the left editor or upload a `.json` file. Malformed input is reported with its line and column before any class is generated.
  2. Read the Generated Classes: The output panel renders `@dataclass` definitions — the root class first, with every nested object extracted into its own class below it.
  3. Copy or Download: Click Copy to paste the classes straight into a module, or Download to save them as a `converted.py` file.

A Worked Example: One Response In, Typed Classes Out

Below is the exact output for a small but realistic payload: a user record containing a nested object, a list of records where one field is present on only some of them, and a header-style key that is not a legal Python attribute name.

Input JSON

{
  "id": 481,
  "username": "ada",
  "rating": 4.75,
  "content-type": "application/json",
  "profile": {
    "city": "London",
    "timezone": "Europe/London"
  },
  "posts": [
    { "id": 7, "title": "Analytical Engine", "views": 3200 },
    { "id": 9, "title": "Note G", "views": 118, "pinned": true }
  ]
}

Generated Python

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


@dataclass
class Root:
    id: int
    username: str
    rating: float
    content_type: str = field(metadata={"json": "content-type"})
    profile: Profile
    posts: list[Post]


@dataclass
class Post:
    id: int
    title: str
    views: int
    pinned: Optional[bool]


@dataclass
class Profile:
    city: str
    timezone: str

Four inference decisions in that output are worth pointing out. `rating` is a `float` while `id` and `views` are `int`, because only one of them carried a decimal point. `profile` was lifted into its own `Profile` class instead of being inlined. The two elements of `posts` were merged into a single `Post` class covering the union of their keys, and because `pinned` appears on only one of them it is typed `Optional[bool]` — the ragged-payload case hand-written models usually get wrong. Finally, `content-type` cannot be an attribute name, so it becomes `content_type` with the original key preserved in the field metadata; identifier-safe keys are left untouched, so a payload made entirely of them can still be constructed with `Root(**payload)`.

Key Capabilities & Features

🐍 Real Dataclasses, Not Dictionaries

`@dataclass` models with PEP 585 annotations for Python 3.9+, giving you a constructor, equality, `repr` and `asdict` out of the box.

🧩 Nested Classes & Shape Deduplication

Nested objects become named classes, and identical shapes collapse into one — a thousand-record array still generates a single class.

🕳️ Honest Nullability

Nulls and keys missing from some records become `Optional[X]` in the annotation only — never a default value that would reorder your constructor.

🔑 Original Keys Preserved

A key that cannot be an attribute name is folded to snake_case, with the exact wire name kept in the field metadata for your deserializer.

Where the Type Inference Stops Guessing

Inferring types from a single sample is a best-effort exercise, and it is far more useful to know exactly where it gives up than to pretend it never does.

  • `int` versus `float`: JavaScript's `JSON.parse` cannot tell `1.0` from `1`, so a price that happened to be `19.0` in your sample is annotated `int`. Widen it by hand where a field is genuinely fractional.
  • Fields that were only ever null: with no other evidence, the attribute is annotated `Any` rather than `Optional[Any]`, since `Any` already admits `None`.
  • Heterogeneous arrays: every branch is kept, so `[1, "two"]` becomes `list[Union[int, str]]`. An empty array has nothing to infer from and becomes `list[Any]`. An array mixing objects with non-objects degrades to a union rather than merging into a class.
  • Very large integers: values beyond 2^53 have already lost precision during parsing, before the generator ever sees them.
  • Documents that are not objects: a top-level array of records generates the element class plus an alias — `Root = list[RootItem]` — so the document itself still has a name you can annotate against.

Treat the result as a very good first draft: exactly right for the sample you supplied, and worth a read-through before it lands in your codebase. If you need a runtime contract rather than static annotations, the JSON Schema generator infers one from the same payload, and if the same shape has to exist in your frontend too, the TypeScript interface generator produces the equivalent declarations from the identical inference pass.

The Sample You Paste Here Is Almost Always Real Production Data

Code generation makes the privacy question sharper than it is for a formatter, and for a structural reason: to get correct types you have to paste a real response. A hand-written toy example produces hand-written toy types, so what actually ends up in the input pane is typically copied straight out of a DevTools Network tab or a `curl` against staging. It carries real customer names, real email addresses, real internal identifiers — and, in the key names alone, a fairly complete map of your internal data model.

That payload never leaves this tab. Your JSON is parsed in a Web Worker inside your browser, and the classes are generated from the parsed value in the same page; no backend endpoint is involved in producing the output, there is no account to create, and there is no ad network sitting in the page alongside your data. Many free online generators do this work server-side instead, which makes the honest answer to "where did my payload go" something closer to "a machine you cannot inspect."

Rather than asking you to take that on trust, the entire project is open source: you or your security team can read the inference and emission code, fork it, or self-host it from the GitHub repository. And if the response you pasted turns out to be malformed before it can be typed at all, the JSON validator will point at the exact line and column that broke it.

Frequently Asked Questions (FAQ)

Does this generate dataclasses or TypedDict?

Dataclasses. Every JSON object becomes a class decorated with `@dataclass`, using PEP 585 annotations (`list[str]` rather than `List[str]`), so the output targets Python 3.9 and up. Dataclasses give you a real constructor, `__repr__`, `__eq__` and `dataclasses.asdict` — and unlike a `TypedDict`, a dataclass field can carry the original wire name for a key that is not a valid Python identifier.

How are null and missing fields typed?

Both become `Optional[X]` in the annotation, and never a default value. Defaults would make field order significant and would quietly turn a required-but-null key into an optional one. A key that appears on only some elements of an array of objects is treated the same way as an explicit null, so a ragged API payload produces `Optional[...]` on exactly the fields that were not always there.

Why was my 1.0 value typed as int instead of float?

The generator runs in your browser, and JavaScript's `JSON.parse` cannot distinguish `1.0` from `1` — both arrive as the same number, so an integral decimal is inferred as `int`. This is a property of JSON parsing rather than of the tool, and it is not recoverable after parsing. If a field is a price, a ratio or any other quantity that can be fractional, widen it to `float` yourself after generating.

What happens to JSON keys that are not valid Python identifiers?

Keys that Python can use verbatim as attribute names are left exactly as they are. Anything else — `content-type`, `2fa`, a reserved word like `class` — is folded to snake_case and the original key is recorded in the field metadata, for example `field(metadata={"json": "content-type"})`, so your deserializer can still map it back. Non-ASCII keys have no ASCII word segments to work with, so the attribute falls back to `value` with the real key still preserved in the metadata.

How is this different from just calling json.loads to get a dict?

`json.loads` gives you a `dict[str, Any]`: correct at runtime, but invisible to your tooling. No editor autocomplete on `payload["profile"]["timezone"]`, no mypy or pyright error when a key is misspelled or removed upstream, and no safe rename. Generating dataclasses puts that same data behind named attributes a type checker can verify, which is what turns an upstream field rename from a production `KeyError` into an error you see while typing.

Is my JSON payload uploaded anywhere?

No. Your JSON is parsed in a background Web Worker and the Python classes are generated from that parsed value inside the same browser tab. There is no backend endpoint involved in producing the output, no account to create, and no ad network in the page. The project is also open source, so the inference and emission code can be read directly.