Generate Python dataclasses from JSON Schema
The JSON Schema → Python Dataclass converter turns a JSON Schema into clean
@dataclass definitions with proper type hints. It is ideal for Python services
that consume LLM structured output or function-calling arguments — the model’s
schema becomes a typed class you can construct and validate against.
How it works
The generator walks the schema and maps JSON types to Python: string→str,
integer→int, number→float, boolean→bool, array→List[T], and
objects to nested dataclasses. String enums become Literal[...]. Fields not in
required are wrapped in Optional[T] with a None default and ordered after
required fields, because a dataclass cannot put a defaulted field before a
non-defaulted one. Nested classes are emitted in dependency order so every
reference resolves.
A concrete example: converting a tool schema
Suppose you are using an LLM with a structured tool call for creating calendar events. The tool’s JSON Schema looks like this:
{
"type": "object",
"required": ["title", "start_time"],
"properties": {
"title": { "type": "string" },
"start_time": { "type": "string" },
"duration_min": { "type": "integer" },
"location": { "type": "string" },
"recurrence": { "type": "string", "enum": ["none","daily","weekly"] }
}
}
The tool converts this to:
from dataclasses import dataclass
from typing import Literal, Optional
@dataclass
class CalendarEvent:
title: str
start_time: str
duration_min: Optional[int] = None
location: Optional[str] = None
recurrence: Optional[Literal["none", "daily", "weekly"]] = None
The required fields (title, start_time) have no default; optional fields
follow with Optional[T] = None; and the enum becomes a Literal type, giving
you IDE autocomplete and runtime checks in one step.
Handling nested objects
When your schema has a nested object, the generator creates a separate
dataclass for the nested type and references it from the parent:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
}
}
Becomes:
@dataclass
class Address:
street: Optional[str] = None
city: Optional[str] = None
@dataclass
class Root:
name: Optional[str] = None
address: Optional[Address] = None
The child class is always declared before the parent so there is no forward-reference issue.
When to use this vs Pydantic
@dataclass is part of the standard library and produces lightweight, readable
classes. It is the right choice when you want zero extra dependencies and are
doing simple construction + attribute access.
If you need runtime validation (raising an error when the LLM returns a wrong
type), coercion (turning a string "42" into an integer 42), or
serialisation back to JSON, reach for Pydantic instead. The shape of the
generated dataclass maps directly to Pydantic’s BaseModel syntax, so porting
between the two is straightforward.
The output includes the needed imports (dataclasses, typing). Everything is
computed locally — nothing you paste leaves the browser.