JSON to Python Pydantic Model

Turn a JSON sample into Pydantic BaseModel classes: typed fields, nested models written before their parents, and output that runs unchanged on Pydantic v1 and v2.

Reaching into a response dictionary by key means every typo and wrong assumption waits until runtime to fail, and a renamed API field turns into a project-wide hunt. Converting a sample response into Pydantic models makes the shape explicit: fields are declared, values are validated when the model is built, and your editor can complete them. A missing key raises a validation error at the boundary instead of turning into a None somewhere downstream.

The conversion runs in the browser and neither the JSON nor the generated models are uploaded. The models describe structure only: no defaults and no aliases, so every field is required. When you are parsing third-party data that may omit keys, you will need to add defaults and optional types yourself — the caveats below say exactly where.

The generated models

Each object becomes a class that inherits BaseModel, and an empty object becomes a class with pass. Nested models are written before their parent, so the file runs top to bottom without forward references or postponed annotations. The header always contains from typing import List, Any and from pydantic import BaseModel. Field annotations use typing.List rather than the builtin list, which validates under both Pydantic v1 and v2, so upgrading later does not mean rewriting the model definitions. The root model is named RootModel unless you change Root type name in the toolbar; a root array of objects becomes a model built from the first element, and a root primitive becomes a plain alias such as RootModel = Any.

Required fields, defaults, and v1 versus v2

No field gets a default value, so under Pydantic every field is required: one missing key and validation fails. Add a default of None, plus an optional type, for keys that may be absent. Note the version difference: v2 no longer treats Optional[X] as an implicit default, so Optional[str] without = None is still required, while adding = None is correct under both v1 and v2. The calling side is where the versions genuinely diverge — v1 uses dict(), parse_obj, an inner Config class and validator; v2 uses model_dump(), model_validate, model_config and field_validator. The generated definitions run on either version; those call sites have to be updated when you migrate.

Key names and type traps

Field names are copied from the JSON verbatim. There is no camelCase to snake_case conversion and no alias, so a camelCase key becomes a camelCase field — valid Python, but not PEP 8. Keys containing a hyphen or a dot produce a syntax error, and so do Python keywords used as keys such as class, from or import; rename those fields and set an alias so validation still finds the original key. Types follow the same inference rules as the other converters: null becomes Any but the field stays required, an empty array becomes List[Any], arrays take their element type from the first item, and integers or decimals with more than about 15 significant digits were already rounded by JSON.parse before the generator ran.

Advertisement

Frequently asked questions

Does the generated code work on Pydantic v2?
The model definitions do: inheriting BaseModel, typing.List annotations and Any are valid in both v1 and v2. The calling code is what breaks. Replace dict(), parse_obj, the Config class and validator with model_dump(), model_validate, model_config and field_validator — nothing inside the generated file has to change.
Why does no field have a default value?
Because the generator reads the structure of one sample and cannot know which keys the API sometimes omits, so it declares every field as required and lets Pydantic enforce that. For a key that may be missing, annotate it as Optional[...] and give it = None. If the API also sends an explicit null, keep None in the type, because a non-optional field rejects it.
Why is a null field typed Any instead of Optional?
A null value does not reveal the type it stands in for, so the generator falls back to Any, which accepts anything and never fails validation — at the cost of all type checking. If you want real constraints, change the annotation to Optional[TheRealType] or TheRealType | None and give it a default of None, so both a missing key and an explicit null are accepted.
Are nested objects separate classes, and does definition order matter?
Yes to the first, and no to the second: each nested object becomes its own BaseModel subclass emitted before its parent, so the file runs without a NameError. Duplicate model names are generated once, first occurrence wins — two same-named branches with different shapes need manual renaming, otherwise the second one silently reuses the first definition.

Related tools

Advertisement