Skip to content

📋 Generate from JSON Schema

Generate Pydantic models from JSON Schema definitions. See Supported Data Types for supported JSON Schema features.

🚀 Quick Start

datamodel-codegen \
    --input person.json \
    --input-file-type jsonschema \
    --output-model-type pydantic_v2.BaseModel \
    --use-annotated \
    --output model.py

📝 Example

person.json

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Person",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "description": "The person's first name."
    },
    "lastName": {
      "type": "string",
      "description": "The person's last name."
    },
    "age": {
      "description": "Age in years which must be equal to or greater than zero.",
      "type": "integer",
      "minimum": 0
    },
    "friends": {
      "type": "array"
    },
    "comment": {
      "type": "null"
    }
  }
}

✨ Generated model.py

# generated by datamodel-codegen:
#   filename:  person.json
#   timestamp: 2020-04-27T16:12:27+00:00

from __future__ import annotations

from typing import Annotated, Any

from pydantic import BaseModel, Field


class Person(BaseModel):
    firstName: Annotated[str | None, Field(description="The person's first name.")] = None
    lastName: Annotated[str | None, Field(description="The person's last name.")] = None
    age: Annotated[
        int | None,
        Field(
            description="Age in years which must be equal to or greater than zero.",
            ge=0,
        ),
    ] = None
    friends: list[Any] | None = None
    comment: Annotated[None, Field(None)] = None

Tuple validation

JSON Schema's prefixItems syntax lets you describe heterogeneous arrays.

When:

  • prefixItems is present
  • no items are specified
  • minItems/maxItems match the number of prefix entries

datamodel-code-generator emits precise tuple annotations.

Example

{
  "$defs": {
    "Span": {
      "type": "object",
      "properties": {
        "value": { "type": "integer" }
      },
      "required": ["value"]
    }
  },
  "title": "defaults",
  "type": "object",
  "properties": {
    "a": {
      "type": "array",
      "prefixItems": [
        { "$ref": "#/$defs/Span" },
        { "type": "string" }
      ],
      "minItems": 2,
      "maxItems": 2
    }
  },
  "required": ["a"]
}
from pydantic import BaseModel


class Span(BaseModel):
    value: int


class Defaults(BaseModel):
    a: tuple[Span, str]

Custom Base Class with customBasePath

You can specify custom base classes directly in your JSON Schema using the customBasePath extension. This allows you to define base classes at the schema level without using CLI options.

Single Base Class

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "User",
  "type": "object",
  "customBasePath": "myapp.models.UserBase",
  "properties": {
    "name": {"type": "string"},
    "email": {"type": "string"}
  },
  "required": ["name", "email"]
}

Generated Output:

from __future__ import annotations

from myapp.models import UserBase


class User(UserBase):
    name: str
    email: str

Multiple Base Classes (Mixins)

You can also specify multiple base classes as a list to implement mixin patterns:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "User",
  "type": "object",
  "customBasePath": ["mixins.AuditMixin", "mixins.TimestampMixin"],
  "properties": {
    "name": {"type": "string"},
    "email": {"type": "string"}
  },
  "required": ["name", "email"]
}

Generated Output:

from __future__ import annotations

from mixins import AuditMixin, TimestampMixin


class User(AuditMixin, TimestampMixin):
    name: str
    email: str

Mixin Usage

When using multiple base classes, the specified classes are used directly without adding BaseModel. Ensure your mixins inherit from pydantic.BaseModel if you need Pydantic model behavior.

Priority Resolution

When multiple base class configurations are present, they are resolved in this order:

  1. --base-class-map (CLI option) - Highest priority
  2. customBasePath (JSON Schema extension)
  3. --base-class (CLI option) - Lowest priority (default for all models)

This allows you to set a default base class with --base-class, override specific models in the schema with customBasePath, and further override at the CLI level with --base-class-map.

Supported JSON Schema Features

Feature Generation behavior
Object properties and required fields Generated as model fields
$ref, $defs, and definitions Resolved before model generation
oneOf, anyOf, allOf Converted into unions and composed models where possible
Scalar constraints Generated as Field(...) metadata with --field-constraints / --use-annotated
format values Mapped to Python/Pydantic types where supported
Custom extensions customBasePath and related options can steer generated base classes

Regular Expression Patterns

Pydantic v2 validates pattern with pydantic-core's Rust regex engine by default, and pydantic-core compiles the pattern when the model class is created. Some syntax that Python's re accepts is rejected by every pydantic-core release, so a model that uses it fails at import. For Pydantic v2 output from JSON Schema and OpenAPI, the generator detects this syntax statically and sets regex_engine="python-re" on the model that owns the pattern. The decision depends only on the pattern text and --target-python-version. It does not depend on the installed pydantic-core or on the Python running the generator, and it is the same for every --target-pydantic-version. regex_engine needs Pydantic 2.5: on Pydantic 2.0 to 2.4 such a model fails at import whatever the target. A pattern in a type alias has no model that owns it, so the alias carries a compiled pattern, or, with --target-pydantic-version 2, every generated model that reaches the alias selects the engine (see Targeting a Pydantic version).

Pattern syntax Example Generated model
Lookahead and lookbehind (?=x), (?<!x) python-re
Numbered backreferences and octal escapes ([a-z])\1, \0, [\101] python-re
Named backreferences (?P<c>x)(?P=c) python-re
Conditionals (<)?a(?(1)>) python-re
End of string \Z python-re
Comments (?#note) python-re
Named characters \N{LATIN SMALL LETTER A} python-re
Backspace in a character class [\b] python-re
ASCII flag (?a), (?a:x), (?ia) python-re
Braces that cannot start a counted repetition a{,3}, {[a-z]+} python-re
Atomic groups (?>a+) python-re with --target-python-version 3.11 or later; unchanged for 3.10
Escaped characters and character class members \\1, \(\?>, [(?>{] Rust engine (default); the lookaround check still matches (?= and (?<! even when escaped or inside a class
Rust-only syntax a{2, 3}, \p{L}, \x{41}, \b{start}, [[a-z]{], (?P<ids[]>x) Rust engine
Syntax both engines reject (?L), ([a-z])\1\p{L} python-re; still fails at import, with Python's error
Possessive quantifiers a++, a*+ Rust engine; pydantic-core accepts them as nested repetitions
Syntax only Pydantic 2.0.0-2.0.2 rejects \/, \:, (?<name>x), [a&&b] Rust engine
Patterns over Rust's compiled size limit [\w.-]{1,256} Rust engine; not detectable from the syntax
Verbose patterns (?x) a b Rust engine; the scan does not model verbose whitespace and comments

Apart from the lookaround check, the scan follows the Rust grammar of every pydantic-core release from 2.3.0 on, so a pattern pydantic-core accepts is never switched. A switched pattern that also contains syntax the target Python's re does not parse keeps the default engine: possessive quantifiers need Python 3.11 and \z needs Python 3.14. The generator does not compile the pattern with Python's re. Lookaround keeps its existing detection for every field. The other rules apply only to patterns emitted for string fields, including unions of strings such as type: ["string", "null"]: a pattern on format: uri (AnyUrl) is not emitted. A pattern on bytes (format: binary) is written only with --field-constraints or --use-annotated. On plain and optional bytes fields Pydantic 2.0 to 2.10 rejects any such pattern at import, Pydantic 2.11 to 2.13 ignores it, and Pydantic 2.14 compiles it like a string pattern, so the rules also apply to it with --target-pydantic-version 2.14. With an unset or older target the model keeps the default engine for its other patterns, and a bytes pattern that the Rust engine rejects fails at import on Pydantic 2.14. Pydantic 2.12 also compiles the pattern of a bytes | MISSING field (--use-missing-sentinel), so such a pattern fails at import there too. A union that has a string member beside bytes, such as str | bytes, follows the string rules for every target. Because regex_engine is a model setting, every pattern in that model then uses Python's re semantics, for example $ also matches before a trailing newline. Python's re is a backtracking engine without the Rust engine's linear-time guarantee: review such patterns, and bound input length (for example with maxLength) when they validate untrusted input.

Limitations

JSON Schema input generates Python model definitions. It does not perform runtime validation by itself, and some schema keywords are represented as type hints or field metadata rather than full JSON Schema validator behavior. See Schema Version Support for version-specific coverage.


📖 See Also