Skip to content

JSON to Python Class

Convert JSON to Python classes in the style your project uses: dataclass, Pydantic model or TypedDict.

Input · JSON
Output · Python
Valid JSONRuns in your browser

Load from a URL

Your browser fetches it directly, so the server must allow cross-origin requests (CORS).

Pick a style

  • Dataclass (the default Style): standard library only, with no validation or conversion.
  • Pydantic: BaseModel subclasses for Pydantic v2 that validate values and build nested models.
  • TypedDict: a type for the plain dict that json.loads returns, read by mypy or Pyright; nothing changes at runtime.

Charge JSON to Python classes

With Charge as the Root name, the sample becomes these dataclasses:

{
  "id": "ch_3QX9",
  "object": "charge",
  "amount": 4200,
  "currency": "usd",
  "paid": true,
  "refunded": false,
  "receipt_email": null,
  "billing_details": {
    "name": "Jenny Rosen",
    "address": { "city": "Berlin", "country": "DE", "postal_code": "10115" }
  },
  "metadata": { "order_id": "6735" },
  "created": 1762300800
}
from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Optional


@dataclass
class Address:
    city: str
    country: str
    postal_code: str


@dataclass
class BillingDetails:
    name: str
    address: Address


@dataclass
class Metadata:
    order_id: str


@dataclass
class Charge:
    id: str
    object: str
    amount: int
    currency: str
    paid: bool
    refunded: bool
    receipt_email: Optional[Any]
    billing_details: BillingDetails
    metadata: Metadata
    created: int

Classes come in dependency order, so Address exists before BillingDetails uses it. receipt_email held only null, hence Optional[Any]; narrow it to Optional[str]. metadata became a class because the sample had one key in it, but payment metadata is usually free-form, so dict[str, str] may fit better.

In a dataclass, a field without a default can’t follow one with a default, so optional fields move to the end with = None. Those are keys missing from some array items, found by merging all items as described on JSON to TypeScript.

Loading data into the classes

Pydantic does the whole job:

from pathlib import Path

body = Path("charge.json").read_text()
charge = Charge.model_validate_json(body)
print(charge.billing_details.address.city)  # Berlin

It builds the nested models, turns "4200" into 4200, and raises ValidationError for wrong types or missing keys. receipt_email must be present but may be null: Pydantic v2 treats an Optional field without a default as required.

A dataclass converts nothing:

import json

charge = Charge(**json.loads(body))
print(type(charge.billing_details))  # <class 'dict'>

Nested objects stay dicts, so build them yourself or switch Style to Pydantic. With TypedDict you only annotate: charge: Charge = json.loads(body). If json.loads fails first, see JSONDecodeError: Expecting value; to inspect a payload, pretty print the JSON in Python.

Keys that aren’t valid Python names

Keywords get a trailing underscore and other keys become snake_case, with the original kept as a Pydantic alias. With Period as the root name:

{ "from": "2026-11-01", "to": "2026-11-30", "time-zone": "Europe/Berlin" }
from __future__ import annotations

from typing import Any, Optional

from pydantic import BaseModel, ConfigDict, Field


class Period(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    from_: str = Field(alias="from")
    to: str
    time_zone: str = Field(alias="time-zone")

populate_by_name=True lets you build the model with either name, as in Period(from_="2026-11-01", to="2026-11-30", time_zone="Europe/Berlin"), and model_dump(by_alias=True) writes from and time-zone back out. Dataclasses have no aliases, so that style leaves a # JSON key: comment and the mapping to you. TypedDict switches to its functional form, TypedDict("Period", {"from": str, ...}), which keeps the exact keys.

Questions

Does the Pydantic style work with Pydantic v1?

No. It targets Pydantic v2, where settings go in model_config = ConfigDict(...) and data is loaded with model_validate. Pydantic v1 used an inner class Config and parse_obj.

Which Python version do I need?

The TypedDict style imports NotRequired from typing, which needs Python 3.11 or newer. On older versions, import it from typing_extensions instead.

How do I load a JSON array of objects?

With Pydantic, use TypeAdapter(list[Charge]).validate_json(body). With dataclasses, build each item yourself, for example in a list comprehension.

Can camelCase keys become snake_case attributes?

Not automatically. Keys that are already valid Python names are kept, so displayName stays displayName. In Pydantic, rename the attribute and set alias_generator=to_camel (from pydantic.alias_generators) in ConfigDict.

Related tools

Esc