JSON to Pydantic Models

Generate Pydantic v2 models from a JSON sample, with nested classes, Optional detection across array items and safe field names. Runs in your browser.

Private by design: everything you enter is processed in your browser. It is not sent to our servers.

How to use

  1. Paste a JSON sample: an object, or an array of objects that all describe the same thing.
  2. Set the name of the top-level model and choose Optional[X] or X | None style.
  3. Copy the generated Pydantic v2 code into your project.
  4. Check the notes under the result: they tell you when field names were changed and aliases were added.

Worked example

A user record with a null value, a nested address and a list of orders where one order has an extra key.

JSON sample

{
  "id": 1,
  "name": "Ada",
  "email": null,
  "tags": ["a", "b"],
  "address": { "city": "Pune", "zip": "411001" },
  "orders": [
    { "sku": "x", "qty": 2, "note": null, "coupon": "A1" },
    { "sku": "y", "qty": 3.5, "note": "gift" }
  ]
}

Generated models

from typing import Any, Optional

from pydantic import BaseModel


class OrdersItem(BaseModel):
    sku: str
    qty: float
    note: Optional[str] = None
    coupon: Optional[str] = None


class Address(BaseModel):
    city: str
    zip: str


class Model(BaseModel):
    id: int
    name: str
    email: Optional[Any] = None
    tags: list[str]
    address: Address
    orders: list[OrdersItem]

email is only ever null in the sample, so it becomes Optional[Any]. note and coupon are Optional because they are null or missing in some orders. qty is float because one order has 3.5. Orders become their own OrdersItem class.

How it works

The generator walks the JSON and infers a type for every value: str, int, float, bool, None, list[…] and nested objects (each nested object becomes its own class, named after its key). Booleans are never treated as integers, and if a field is sometimes int and sometimes float it becomes float.

When the sample is an array of objects, all elements are merged into one class. A key that is missing from some elements, or that is null in some, becomes Optional with a default of None. A field with truly different types becomes Union[…] (or X | Y).

Field names are made valid Python identifiers (snake_case, keywords get a trailing underscore, names that clash with Pydantic’s model_ attributes are prefixed). Whenever the name changes, the field gets Field(alias="original-key") so validation still accepts the original JSON key, as described in the Pydantic documentation.

The output uses Pydantic v2 style (BaseModel and Field from pydantic) with built-in generics such as list[str]. Nested classes are written before the classes that use them so the file runs top to bottom.

Common errors

Invalid JSON: …
Fix the syntax first (quotes, commas, brackets). The JSON Formatter style rules apply: double quotes only, no trailing commas.
The sample must be a JSON object, or a non-empty array of objects.
Wrap a bare value in an object, or give an array whose elements are all objects.
The model name must be a valid Python class name.
Use letters and digits, for example “User” or “OrderResponse”.
Input is too large (limit 1,000,000 characters).
Use a smaller representative sample.

Limits

  • Types come from the sample only. A field that is always a string in your sample but sometimes a number in production will be typed str.
  • Strings that look like dates, UUIDs or emails stay str; no datetime, UUID or constraint types are guessed.
  • An empty list becomes list[Any] because there is nothing to learn the item type from.
  • Keys that differ only in case or punctuation can collide after renaming; the generator appends underscores to keep names unique.
  • Arrays that mix objects with scalars are not merged into one class.

Privacy

Your JSON is analysed in your browser and never sent anywhere. Share links include the JSON only if it is short. No account is needed, no cookies are set and no analytics run. See thePrivacy Policy. The “Copy share link” button puts your settings after the “#” in the address; browsers do not send that part to servers, and the page’s canonical address never includes it.

FAQ

Why Optional[Any] for a field that is always null?

A sample that only contains null gives no information about the real type, so Any is the honest answer. Replace it once you know the type.

Does it support Pydantic v1?

No, the output targets Pydantic v2 (it uses Field(alias=…) and built-in generics).

Why did my camelCase keys get aliases?

Python fields are conventionally snake_case, so userId becomes user_id with alias="userId" so incoming JSON still validates.

Should I trust the generated types?

Treat them as a first draft. Review the Optional and Union choices against your real data and API documentation.

Embed this tool

Paste this snippet into your own page or docs to show the tool inline. The embedded view works the same way (everything runs in the visitor’s browser) and carries a small link back to MakeTools.

Sources

Page last updated 2026-10-14. Written by Prajyot Mulik; see the editorial policy.