JSON to Python: dataclass vs TypedDict vs Pydantic
You have JSON and you want typed Python objects instead of raw dicts. There are three good targets — dataclass, TypedDict and Pydantic — and they solve different problems. Here is how to choose.
The shape
Every example below models the same tiny record:
{ "id": 1, "name": "Ada", "active": true }
dataclass — plain typed objects
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
active: bool
You get attribute access (user.name) and clean equality, but no validation — you still parse the JSON yourself with User(**json.loads(s)). Best for internal data you already trust.
TypedDict — a dict with a known shape
from typing import TypedDict
class User(TypedDict):
id: int
name: str
active: bool
It stays a plain dict at runtime (user["name"]) — the types exist only for the type-checker, with no runtime validation. Great when you want to keep the data as dicts but still get editor and mypy support.
Pydantic — validation at the boundary
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
active: bool
user = User.model_validate_json(s) # raises on bad data
Pydantic parses and validates: wrong types raise an error instead of corrupting silently. This is the right choice for untrusted input — API request bodies, config files, webhooks.
Generate the class automatically
Instead of typing the class out by hand, paste a JSON sample into the JSON to Python converter and it generates the class for whichever style you pick — 100% in your browser, nothing uploaded.
How to choose
- Trusted internal data, want objects →
dataclass. - Keep it as dicts, just want type hints →
TypedDict. - Data crossing a trust boundary → Pydantic (validate once, trust after).
One caveat applies to every generator: it infers types from a single example, so it cannot see nullability, optional fields, or unions. Read the output before you ship it — a field that is null in your sample is probably Optional in reality.