new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Pydantic · Concepts · all subjects

json schema

42 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

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+.

Give your agent this brain