JSON to Pydantic Model Generator

Generate Pydantic v2 BaseModel classes from JSON data with automatic type inference, nested models, and modern Python type hints.

What is JSON to Pydantic Model Generator?

Pydantic is the de-facto data-validation layer for modern Python — it powers FastAPI request bodies, validates settings in production services, and turns untrusted JSON into typed, validated Python objects. This generator emits Pydantic v2 `BaseModel` classes, which differ meaningfully from the v1 classes most older tutorials show. In v2 the validation core was rewritten in Rust (pydantic-core), `@validator` became `@field_validator`, `@root_validator` became `@model_validator`, the inner `class Config` gave way to `model_config = ConfigDict(...)`, and `.dict()`/`.json()` were renamed to `.model_dump()`/`.model_dump_json()`. If you paste v1-era decorators into a v2 project you get deprecation warnings or hard errors, so generating v2-correct scaffolding from the start saves real migration pain. Unlike a plain `json-to-python` dataclass generator, the output here is runtime-validating: every field is type-checked, coerced, and error-reported when you call `Model.model_validate(data)`. The generator infers `str`, `int`, `float`, `bool`, nested models, `list[T]`, and `T | None` from your sample, converts camelCase JSON keys to snake_case Python attributes with `Field(alias=...)`, and orders nested models so the file imports cleanly with no forward-reference juggling.

How to Use

  1. Paste a real API response (not a hand-typed example) so every field gets the type it actually carries at runtime — a numeric string like "42" will be inferred as str, which is usually what you want for IDs
  2. Set the root class name to match your domain (e.g. "CheckoutRequest" for a FastAPI @app.post body) — this is the class you will pass to model_validate or annotate the endpoint parameter with
  3. Enable snake_case conversion when your JSON uses camelCase keys: the generator emits snake_case attributes plus Field(alias="originalKey") and you add model_config = ConfigDict(populate_by_name=True) so both forms deserialize
  4. After generating, attach @field_validator("email") or @computed_field for any business rules — the generator gives you the typed skeleton, you add the v2-style validators on top
  5. Drop the classes into your FastAPI app and use them directly as request/response models; FastAPI reads the Pydantic schema to produce OpenAPI docs automatically

Why Use This Tool?

Emits Pydantic v2 syntax (ConfigDict, list[T], T | None) — no v1 deprecation warnings to clean up later
Generates runtime-validating models, not just type hints: bad data raises ValidationError with a precise loc path
Handles camelCase-to-snake_case with Field(alias=...) so JSON keys and Pythonic attribute names coexist
Orders nested models so the generated module imports without NameError or forward-reference strings
Plugs straight into FastAPI for automatic request validation and OpenAPI schema generation
Runs fully in the browser — your production payloads never touch a server

Tips & Best Practices

  • In v2, migrate @validator to @field_validator and add the @classmethod decorator under it — field_validator does not implicitly make the method a classmethod the way v1 sometimes appeared to
  • Use @computed_field with @property for derived values (e.g. full_name from first + last) — it shows up in model_dump() output, unlike a plain @property
  • For cross-field checks (password == confirm_password) use @model_validator(mode="after"), which runs on the fully constructed instance; mode="before" runs on the raw dict before field validation
  • Set model_config = ConfigDict(extra="forbid") when you want unexpected JSON keys to raise instead of being silently dropped — invaluable for catching typos in client payloads
  • For ISO timestamps, annotate with datetime and Pydantic v2 parses the string automatically; you do not need a validator, but you do need to import datetime
  • Call .model_validate(data) not the old .parse_obj(), and .model_dump(by_alias=True) to serialize back to the original camelCase keys

Frequently Asked Questions

What is the full JSON type to Pydantic type mapping?

JSON string -> str (or datetime if you annotate it and the string is ISO-8601). JSON integer -> int. JSON float -> float. JSON boolean -> bool. JSON null -> Optional[T] written as T | None. JSON array -> list[T] where T is inferred from the first element. JSON object -> a separate nested BaseModel class. An empty array becomes list[Any]; a heterogeneous array falls back to list[Any] as well.

How do I migrate the generated v2 code if my project is still on Pydantic v1?

Replace model_config = ConfigDict(...) with an inner class Config. Change @field_validator("x") @classmethod back to @validator("x"). Change @model_validator(mode="after") to @root_validator. Replace .model_dump() with .dict() and .model_validate() with .parse_obj(). The field type hints and Field(alias=...) calls are identical between versions, so most of the work is the validators and config.

What replaced @validator and @root_validator in Pydantic v2?

@validator became @field_validator, which validates a single field and must be paired with @classmethod. @root_validator became @model_validator, which validates the whole model and takes a mode argument: mode="before" receives the raw input dict, mode="after" receives the constructed model instance. The old pre=True / always=True keyword arguments are gone — use the mode parameter instead.

When should I use @computed_field?

Use @computed_field (combined with @property) when a value is derived from other fields and you want it included in serialization output. A plain @property is accessible in Python but is excluded from model_dump() and the JSON schema; @computed_field includes it in both, so API consumers see the derived value. It is read-only and recomputed on each access.

Why are camelCase keys converted to snake_case with aliases?

PEP 8 mandates snake_case for Python attributes, but JSON APIs frequently use camelCase. The generator emits snake_case attributes and adds Field(alias="camelCaseKey"). With model_config = ConfigDict(populate_by_name=True), the model accepts both the alias and the Python name on input, and model_dump(by_alias=True) emits the camelCase form on output — so you write idiomatic Python without breaking the wire format.

Is my data sent to a server?

No. All model generation runs in your browser via JavaScript. Your JSON never leaves your device.

Real-world Examples

A FastAPI checkout request body with nested address

You are building a FastAPI POST endpoint and want a validated request model. Pasting the JSON below with snake_case conversion produces a v2 BaseModel pair you can annotate the endpoint with directly; FastAPI then validates the body and documents it in OpenAPI for free.

Input
{
  "orderId": "ord_99213",
  "customerEmail": "[email protected]",
  "totalAmount": 149.95,
  "itemCount": 3,
  "isGift": false,
  "shippingAddress": {
    "line1": "742 Evergreen Terrace",
    "city": "Springfield",
    "postalCode": "62701"
  }
}
Output
from pydantic import BaseModel, ConfigDict, Field


class ShippingAddress(BaseModel):
    line1: str
    city: str
    postal_code: str = Field(alias="postalCode")

    model_config = ConfigDict(populate_by_name=True)


class CheckoutRequest(BaseModel):
    order_id: str = Field(alias="orderId")
    customer_email: str = Field(alias="customerEmail")
    total_amount: float = Field(alias="totalAmount")
    item_count: int = Field(alias="itemCount")
    is_gift: bool = Field(alias="isGift")
    shipping_address: ShippingAddress = Field(alias="shippingAddress")

    model_config = ConfigDict(populate_by_name=True)

Adding a v2 field_validator after generation

The generator gives you the typed skeleton; you add the business rules. Here a profile model gets a v2 @field_validator to normalize the username and a @model_validator to enforce a cross-field rule — note the @classmethod requirement and the mode="after" instance check that did not exist in v1 the same way.

Input
{
  "username": "Jane_Doe",
  "age": 30,
  "newsletterOptIn": true,
  "lastLogin": null
}
Output
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator


class Profile(BaseModel):
    username: str
    age: int
    newsletter_opt_in: bool = Field(alias="newsletterOptIn")
    last_login: datetime | None = Field(default=None, alias="lastLogin")

    model_config = ConfigDict(populate_by_name=True)

    @field_validator("username")
    @classmethod
    def normalize_username(cls, v: str) -> str:
        return v.strip().lower()

    @model_validator(mode="after")
    def check_age(self) -> "Profile":
        if self.age < 13:
            raise ValueError("user must be at least 13")
        return self

Related Tools