Generating JSON schema from BaseModel and TypeAdapter
Use BaseModel.model_json_schema() to return a jsonable dict of a model's schema. Use TypeAdapter.json_schema() to return a jsonable dict of an adapted type's schema. These methods return a jsonable dict representing the JSON schema, not a JSON string. Calling json.dumps() on the result produces a valid JSON string.
JSON Schema compliance standards
Pydantic generates JSON schemas that are compliant with JSON Schema Draft 2020-12 and OpenAPI Specification v3.1.0.
JsonSchemaMode parameter values
The mode parameter in model_json_schema() and TypeAdapter.json_schema() accepts two values: 'validation' (default) which produces a JSON schema corresponding to the model's validation schema, and 'serialization' which produces a JSON schema corresponding to serialization behavior.
Field-level JSON Schema customization with Field()
The Field() function accepts these parameters for JSON Schema customization: title (the title of the field), description (the description of the field), examples (examples of the field), json_schema_extra (extra JSON Schema properties to add to the field), and field_title_generator (a function that programmatically sets the field's title based on its name and info).
Model-level JSON Schema customization with ConfigDict
ConfigDict accepts these options for JSON Schema customization: title (model title), json_schema_extra (extra JSON Schema properties), json_schema_mode_override (override the JSON schema mode), field_title_generator (function to programmatically set field titles), and model_title_generator (function to programmatically set the model title).
json_schema_extra accepts dict or callable
The json_schema_extra option can accept either a dict to add extra information to the JSON schema, or a Callable that receives the schema dict and modifies it. When using Callable, the function modifies the schema in place and may not return anything. Mixing dict and callable json_schema_extra specifications is not supported.
Merging json_schema_extra from annotated types
Starting in Pydantic v2.9, json_schema_extra dictionaries from annotated types are merged additively rather than overriding. This applies when combining json_schema_extra from multiple annotations on the same type.
WithJsonSchema annotation for custom types
The WithJsonSchema annotation is preferred over implementing __get_pydantic_json_schema__() for custom types. It overrides the entire generated JSON Schema for a type. When using WithJsonSchema, you must provide the complete schema including the 'type' field. It is useful for types that don't produce any JSON schemas by default, such as Callable.
SkipJsonSchema annotation
The SkipJsonSchema annotation can be used to skip an included field or part of a field's specifications from the generated JSON schema.
__get_pydantic_core_schema__ for custom types and metadata
Custom types and Annotated metadata can implement __get_pydantic_core_schema__(source, handler) to modify or override the generated core schema. The method receives the type annotation and a handler callback. For custom types, you typically should not call handler(source) as it will fail for arbitrary types. For Annotated metadata, you can usually call handler(source) to have Pydantic generate the base schema, then modify or wrap it. You can modify the schema in place, wrap it, or return a completely custom CoreSchema.
__get_pydantic_json_schema__ for JSON schema modification
Implement __get_pydantic_json_schema__(core_schema, handler) to modify or override the generated JSON schema. This only affects the JSON schema, not the core schema used for validation and serialization. Call handler(core_schema) to get the base JSON schema, then modify it. Call handler.resolve_ref_schema(json_schema) to resolve any $ref references in the schema.
Customizing JSON schema with GenerateJsonSchema
Pass a custom GenerateJsonSchema subclass to model_json_schema(), TypeAdapter.json_schema(), or models_json_schema() via the schema_generator parameter to customize the entire JSON schema generation process. Override methods in the subclass to modify schema generation. The GenerateJsonSchema class breaks JSON schema generation into smaller methods that can be overridden.
JSON schema sorting behavior
By default, Pydantic recursively sorts JSON schemas by alphabetically sorting keys, except it skips sorting the values of the 'properties' key to preserve field definition order. Override the sort() method in a custom GenerateJsonSchema subclass to customize this behavior.
Customizing $ref format with ref_template
Pass the ref_template keyword argument to model_json_schema() or TypeAdapter.json_schema() to customize the format of $ref values. The format string should use {model} as a placeholder for the model name. Definitions are always stored under the $defs key. For example, ref_template='#/components/schemas/{model}' changes references to OpenAPI format.
Top-level schema generation with models_json_schema()
Use models_json_schema() to generate a top-level JSON schema that only includes a list of models and related sub-models in its $defs. Pass a list of tuples containing (Model, mode) pairs and a title parameter. Returns a tuple of (definitions_dict, top_level_schema).
JSON schema for Optional fields
The JSON schema for Optional fields indicates that the value null is allowed, typically using anyOf with null type.
Decimal type in JSON schema
The Decimal type is exposed in JSON schema and serialized as a string.
Sub-models in JSON schema
Sub-models used in a model are added to the $defs JSON attribute and referenced according to the JSON Schema spec. Sub-models with modifications via Field (such as custom title, description, or default value) are recursively included instead of referenced.
Model description in JSON schema
The description for models in JSON schema is taken from either the docstring of the class or the description argument to the Field class.
Aliases in JSON schema generation
JSON schema is generated by default using aliases as keys. Pass by_alias=False to model_json_schema() or model_dump_json() to generate schema using model property names instead of aliases.
namedtuple not preserved in JSON schema
Since the namedtuple type doesn't exist in JSON, a model's JSON schema does not preserve namedtuples as namedtuples.
field_title_generator for programmatic field titles
The field_title_generator parameter accepts a callable that takes (field_name: str, field_info: FieldInfo) and returns a string for the field title. This can be set at the field level via Field(field_title_generator=...) or model level via ConfigDict(field_title_generator=...). The model-level setting applies to all fields.
model_title_generator for programmatic model titles
The model_title_generator config option accepts a callable that takes the model class and returns a string for the model title. This is set via ConfigDict(model_title_generator=...).
Type mapping priority for JSON schema
Types, custom field types, and constraints are mapped to JSON schema formats in this priority order: 1) JSON Schema Core, 2) JSON Schema Validation, 3) OpenAPI Data Types, 4) the standard 'format' JSON field for Pydantic extensions for complex string sub-types.
Example: Generate JSON schema from BaseModel
```python
import json
from pydantic import BaseModel, Field
from pydantic.config import ConfigDict
class MainModel(BaseModel):
"""This is the description of the main model"""
model_config = ConfigDict(title='Main')
snap: int = Field(
default=42,
title='The Snap',
description='this is the value of snap',
gt=30,
lt=50,
)
main_model_schema = MainModel.model_json_schema()
print(json.dumps(main_model_schema, indent=2))
```
Example: Generate JSON schema from TypeAdapter
```python
from pydantic import TypeAdapter
adapter = TypeAdapter(list[int])
print(adapter.json_schema())
#> {'items': {'type': 'integer'}, 'type': 'array'}
```
Example: JSON schema mode validation vs serialization
```python
from decimal import Decimal
from pydantic import BaseModel
class Model(BaseModel):
a: Decimal = Decimal('12.34')
# Validation mode includes anyOf with number and string pattern
print(Model.model_json_schema(mode='validation'))
# Serialization mode only includes string type with pattern
print(Model.model_json_schema(mode='serialization'))
```
Example: Field-level JSON Schema customization
```python
import json
from typing import Annotated
from pydantic import BaseModel, EmailStr, Field, SecretStr
class User(BaseModel):
age: int = Field(description='Age of the user')
email: Annotated[EmailStr, Field(examples=['marcelo@mail.com'])]
name: str = Field(title='Username')
password: SecretStr = Field(
json_schema_extra={
'title': 'Password',
'description': 'Password of the user',
'examples': ['123456'],
}
)
print(json.dumps(User.model_json_schema(), indent=2))
```
Example: json_schema_extra with dict
```python
import json
from pydantic import BaseModel, ConfigDict
class Model(BaseModel):
a: str
model_config = ConfigDict(json_schema_extra={'examples': [{'a': 'Foo'}]})
print(json.dumps(Model.model_json_schema(), indent=2))
```
Example: json_schema_extra with callable
```python
import json
from pydantic import BaseModel, Field
def pop_default(s):
s.pop('default')
class Model(BaseModel):
a: int = Field(default=1, json_schema_extra=pop_default)
print(json.dumps(Model.model_json_schema(), indent=2))
```
Example: WithJsonSchema annotation
```python
import json
from typing import Annotated
from pydantic import BaseModel, WithJsonSchema
MyInt = Annotated[
int,
WithJsonSchema({'type': 'integer', 'examples': [1, 0, -1]}),
]
class Model(BaseModel):
a: MyInt
print(json.dumps(Model.model_json_schema(), indent=2))
```
Example: Custom type with __get_pydantic_core_schema__
```python
from dataclasses import dataclass
from typing import Any
from pydantic_core import core_schema
from pydantic import BaseModel, GetCoreSchemaHandler
@dataclass
class CompressedString:
dictionary: dict[int, str]
text: list[int]
def build(self) -> str:
return ' '.join([self.dictionary[key] for key in self.text])
@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
assert source is CompressedString
return core_schema.no_info_after_validator_function(
cls._validate,
core_schema.str_schema(),
serialization=core_schema.plain_serializer_function_ser_schema(
cls._serialize,
info_arg=False,
return_schema=core_schema.str_schema(),
),
)
@staticmethod
def _validate(value: str) -> 'CompressedString':
inverse_dictionary: dict[str, int] = {}
text: list[int] = []
for word in value.split(' '):
if word not in inverse_dictionary:
inverse_dictionary[word] = len(inverse_dictionary)
text.append(inverse_dictionary[word])
return CompressedString(
{v: k for k, v in inverse_dictionary.items()}, text
)
@staticmethod
def _serialize(value: 'CompressedString') -> str:
return value.build()
class MyModel(BaseModel):
value: CompressedString
print(MyModel.model_json_schema())
```
Example: Annotated metadata with __get_pydantic_core_schema__
```python
from collections.abc import Sequence
from dataclasses import dataclass
from typing import Annotated, Any
from pydantic_core import core_schema
from pydantic import BaseModel, GetCoreSchemaHandler, ValidationError
@dataclass
class RestrictCharacters:
alphabet: Sequence[str]
def __get_pydantic_core_schema__(
self, source: type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
if not self.alphabet:
raise ValueError('Alphabet may not be empty')
schema = handler(source)
if schema['type'] != 'str':
raise TypeError('RestrictCharacters can only be applied to strings')
return core_schema.no_info_after_validator_function(
self.validate,
schema,
)
def validate(self, value: str) -> str:
if any(c not in self.alphabet for c in value):
raise ValueError(
f'{value!r} is not restricted to {self.alphabet!r}'
)
return value
class MyModel(BaseModel):
value: Annotated[str, RestrictCharacters('ABC')]
print(MyModel.model_json_schema())
print(MyModel(value='CBA'))
```
Example: __get_pydantic_json_schema__ implementation
```python
import json
from typing import Any
from pydantic_core import core_schema as cs
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler, TypeAdapter
from pydantic.json_schema import JsonSchemaValue
class Person:
name: str
age: int
def __init__(self, name: str, age: int):
self.name = name
self.age = age
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> cs.CoreSchema:
return cs.typed_dict_schema({
'name': cs.typed_dict_field(cs.str_schema()),
'age': cs.typed_dict_field(cs.int_schema()),
})
@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: cs.CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
json_schema = handler(core_schema)
json_schema = handler.resolve_ref_schema(json_schema)
json_schema['examples'] = [{'name': 'John Doe', 'age': 25}]
json_schema['title'] = 'Person'
return json_schema
print(json.dumps(TypeAdapter(Person).json_schema(), indent=2))
```
Example: Custom GenerateJsonSchema subclass
```python
from pydantic import BaseModel
from pydantic.json_schema import GenerateJsonSchema
class MyGenerateJsonSchema(GenerateJsonSchema):
def generate(self, schema, mode='validation'):
json_schema = super().generate(schema, mode=mode)
json_schema['title'] = 'Customize title'
json_schema['$schema'] = self.schema_dialect
return json_schema
class MyModel(BaseModel):
x: int
print(MyModel.model_json_schema(schema_generator=MyGenerateJsonSchema))
```
Example: Excluding invalid fields from JSON schema
```python
from collections.abc import Callable
from pydantic_core import PydanticOmit, core_schema
from pydantic import BaseModel
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
class MyGenerateJsonSchema(GenerateJsonSchema):
def handle_invalid_for_json_schema(
self, schema: core_schema.CoreSchema, error_info: str
) -> JsonSchemaValue:
raise PydanticOmit
def example_callable():
return 1
class Example(BaseModel):
name: str = 'example'
function: Callable = example_callable
instance_example = Example()
validation_schema = instance_example.model_json_schema(
schema_generator=MyGenerateJsonSchema, mode='validation'
)
print(validation_schema)
```
Example: Disabling JSON schema sorting
```python
import json
from pydantic import BaseModel, Field
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
class MyGenerateJsonSchema(GenerateJsonSchema):
def sort(
self, value: JsonSchemaValue, parent_key: str | None = None
) -> JsonSchemaValue:
"""No-op, we don't want to sort schema values at all."""
return value
class Bar(BaseModel):
c: str
b: str
a: str = Field(json_schema_extra={'c': 'hi', 'b': 'hello', 'a': 'world'})
json_schema = Bar.model_json_schema(schema_generator=MyGenerateJsonSchema)
print(json.dumps(json_schema, indent=2))
```
Example: Customizing $ref format for OpenAPI
```python
import json
from pydantic import BaseModel
from pydantic.type_adapter import TypeAdapter
class Foo(BaseModel):
a: int
class Model(BaseModel):
a: Foo
adapter = TypeAdapter(Model)
print(
json.dumps(
adapter.json_schema(ref_template='#/components/schemas/{model}'),
indent=2,
)
)
```
Example: Top-level schema with models_json_schema()
```python
import json
from pydantic import BaseModel
from pydantic.json_schema import models_json_schema
class Foo(BaseModel):
a: str = None
class Model(BaseModel):
b: Foo
class Bar(BaseModel):
c: int
_, top_level_schema = models_json_schema(
[(Model, 'validation'), (Bar, 'validation')], title='My Schema'
)
print(json.dumps(top_level_schema, indent=2))
```
Example: field_title_generator for field titles
```python
import json
from pydantic import BaseModel, Field
from pydantic.fields import FieldInfo
def make_title(field_name: str, field_info: FieldInfo) -> str:
return field_name.upper()
class Person(BaseModel):
name: str = Field(field_title_generator=make_title)
age: int = Field(field_title_generator=make_title)
print(json.dumps(Person.model_json_schema(), indent=2))
```
Example: model_title_generator for model title
```python
import json
from pydantic import BaseModel, ConfigDict
def make_title(model: type) -> str:
return f'Title-{model.__name__}'
class Person(BaseModel):
model_config = ConfigDict(model_title_generator=make_title)
name: str
age: int
print(json.dumps(Person.model_json_schema(), indent=2))
```
Named type aliases and their JSON Schema benefits
Named type aliases, introduced in Pydantic V2.11 and leveraging PEP 695, provide two key benefits over implicit type aliases: (1) The JSON Schema of the alias is converted into a definition using $defs, which is useful when the alias is used multiple times in a model definition, and (2) recursive type aliases become possible. Named aliases can be created using TypeAliasType for Python 3.10+ or the type statement for Python 3.12+.