コンテンツにスキップ

JSON Schema

??? API「APIドキュメント」 pydantic.json_schema

Pydantic を使用すると、モデルから JSON スキーマを自動的に作成およびカスタマイズできます。生成された JSON スキーマは次の仕様に準拠しています。

JSONスキーマの生成

JSON スキーマを生成するには、次の関数を使用します。

!!! note これらのメソッドは、それぞれモデルまたは適応型のインスタンスをシリアル化する BaseModel.model_dump_json および TypeAdapter.dump_json と混同しないでください。 。これらのメソッドは JSON 文字列を返します。比較すると、BaseModel.model_json_schemaTypeAdapter.json_schema は、それぞれモデルまたは適応型の JSON スキーマを表す jsonable dict を返します。

!!!注「JSON スキーマの "jsonable" の性質について」 model_json_schema の結果の "jsonable" の性質に関して、呼び出し json.dumps(m.model_json_schema()) 一部のBaseModelでは、 m有効な JSON 文字列を返します。同様に、TypeAdapter.json_schema の場合は、 json.dumps(TypeAdapter(<some_type>).json_schema()) 有効な JSON 文字列を返します。

!!!ヒント Pydantic は次の両方のサポートを提供します。

1. [Customizing JSON Schema](#customizing-json-schema)
2. [Customizing the JSON Schema Generation Process](#customizing-the-json-schema-generation-process)

The first approach generally has a more narrow scope, allowing for customization of the JSON schema for
more specific cases and types. The second approach generally has a more broad scope, allowing for customization
of the JSON schema generation process overall. The same effects can be achieved with either approach, but
depending on your use case, one approach might offer a more simple solution than the other.

BaseModelから JSON スキーマを生成する例を次に示します。

import json
from enum import Enum
from typing import Union

from typing_extensions import Annotated

from pydantic import BaseModel, Field
from pydantic.config import ConfigDict


class FooBar(BaseModel):
    count: int
    size: Union[float, None] = None


class Gender(str, Enum):
    male = 'male'
    female = 'female'
    other = 'other'
    not_given = 'not_given'


class MainModel(BaseModel):
    """
    This is the description of the main model
    """

    model_config = ConfigDict(title='Main')

    foo_bar: FooBar
    gender: Annotated[Union[Gender, None], Field(alias='Gender')] = None
    snap: int = Field(
        42,
        title='The Snap',
        description='this is the value of snap',
        gt=30,
        lt=50,
    )


main_model_schema = MainModel.model_json_schema()  # (1)!
print(json.dumps(main_model_schema, indent=2))  # (2)!
"""
{
  "$defs": {
    "FooBar": {
      "properties": {
        "count": {
          "title": "Count",
          "type": "integer"
        },
        "size": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Size"
        }
      },
      "required": [
        "count"
      ],
      "title": "FooBar",
      "type": "object"
    },
    "Gender": {
      "enum": [
        "male",
        "female",
        "other",
        "not_given"
      ],
      "title": "Gender",
      "type": "string"
    }
  },
  "description": "This is the description of the main model",
  "properties": {
    "foo_bar": {
      "$ref": "#/$defs/FooBar"
    },
    "Gender": {
      "anyOf": [
        {
          "$ref": "#/$defs/Gender"
        },
        {
          "type": "null"
        }
      ],
      "default": null
    },
    "snap": {
      "default": 42,
      "description": "this is the value of snap",
      "exclusiveMaximum": 50,
      "exclusiveMinimum": 30,
      "title": "The Snap",
      "type": "integer"
    }
  },
  "required": [
    "foo_bar"
  ],
  "title": "Main",
  "type": "object"
}
"""
```

1. This produces a "jsonable" dict of `MainModel`'s schema.
2. Calling `json.dumps` on the schema dict produces a JSON string.

The [`TypeAdapter`][pydantic.type_adapter.TypeAdapter] class lets you create an object with methods for validating, serializing,
and producing JSON schemas for arbitrary types. This serves as a complete replacement for `schema_of` in
Pydantic V1 (which is now deprecated).

Here's an example of generating JSON schema from a [`TypeAdapter`][pydantic.type_adapter.TypeAdapter]:

```py
from typing import List

from pydantic import TypeAdapter

adapter = TypeAdapter(List[int])
print(adapter.json_schema())
#> {'items': {'type': 'integer'}, 'type': 'array'}
```

You can also generate JSON schemas for combinations of [`BaseModel`s][pydantic.main.BaseModel]
and [`TypeAdapter`s][pydantic.type_adapter.TypeAdapter], as shown in this example:

```py
import json
from typing import Union

from pydantic import BaseModel, TypeAdapter


class Cat(BaseModel):
    name: str
    color: str


class Dog(BaseModel):
    name: str
    breed: str


ta = TypeAdapter(Union[Cat, Dog])
ta_schema = ta.json_schema()
print(json.dumps(ta_schema, indent=2))
"""
{
  "$defs": {
    "Cat": {
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        },
        "color": {
          "title": "Color",
          "type": "string"
        }
      },
      "required": [
        "name",
        "color"
      ],
      "title": "Cat",
      "type": "object"
    },
    "Dog": {
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        },
        "breed": {
          "title": "Breed",
          "type": "string"
        }
      },
      "required": [
        "name",
        "breed"
      ],
      "title": "Dog",
      "type": "object"
    }
  },
  "anyOf": [
    {
      "$ref": "#/$defs/Cat"
    },
    {
      "$ref": "#/$defs/Dog"
    }
  ]
}
"""

JsonSchemaModeの構成

model_json_schema および TypeAdapter.json_schema メソッドのmodeパラメーターを介して JSON スキーマ生成のモードを指定します。デフォルトでは、モードは'validation'に設定されており、モデルの検証スキーマに対応する JSON スキーマが生成されます。

JsonSchemaMode は、 modeパラメーターで使用可能なオプションを表す型エイリアスです。

  • 'validation'
  • 'serialization'

ここでは、 modeパラメーターを指定する方法と、それが生成された JSON スキーマにどのような影響を与えるかを示す例を示します。

from decimal import Decimal

from pydantic import BaseModel


class Model(BaseModel):
    a: Decimal = Decimal('12.34')


print(Model.model_json_schema(mode='validation'))
"""
{
    'properties': {
        'a': {
            'anyOf': [{'type': 'number'}, {'type': 'string'}],
            'default': '12.34',
            'title': 'A',
        }
    },
    'title': 'Model',
    'type': 'object',
}
"""

print(Model.model_json_schema(mode='serialization'))
"""
{
    'properties': {'a': {'default': '12.34', 'title': 'A', 'type': 'string'}},
    'title': 'Model',
    'type': 'object',
}
"""

JSON スキーマのカスタマイズ

生成された JSON スキーマは、次の方法でフィールド レベルとモデル レベルの両方でカスタマイズできます。

  1. Field コンストラクターを使用したフィールドレベルのカスタマイズ
  2. model_config によるモデルレベルのカスタマイズ

フィールド レベルとモデル レベルの両方で、 json_schema_extraオプションを使用して JSON スキーマに追加情報を追加できます。このオプションの詳細については、以下のjson_schema_extraの使用」セクションで説明します。

カスタム型の場合、Pydantic は JSON スキーマ生成をカスタマイズするための他のツールを提供します。

  1. WithJsonSchemaアノテーション
  2. SkipJsonSchemaアノテーション
  3. __get_pydantic_core_schema__の実装
  4. __get_pydantic_json_schema__の実装

フィールドレベルのカスタマイズ

オプションで、 Field 関数を使用して、フィールドと検証に関する追加情報を提供できます。

一部のフィールド パラメーターは、生成された JSON スキーマをカスタマイズするためにのみ使用されます。

  • title : フィールドのタイトル。
  • description : フィールドの説明。
  • examples : フィールドの例。
  • json_schema_extra : フィールドに追加される追加の JSON スキーマ プロパティ。
  • field_title_generator : フィールドの名前と情報に基づいて、プログラムによってフィールドのタイトルを設定する関数。

以下に例を示します。

import json

from pydantic import BaseModel, EmailStr, Field, SecretStr


class User(BaseModel):
    age: int = Field(description='Age of the user')
    email: 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))
"""
{
  "properties": {
    "age": {
      "description": "Age of the user",
      "title": "Age",
      "type": "integer"
    },
    "email": {
      "examples": [
        "marcelo@mail.com"
      ],
      "format": "email",
      "title": "Email",
      "type": "string"
    },
    "name": {
      "title": "Username",
      "type": "string"
    },
    "password": {
      "description": "Password of the user",
      "examples": [
        "123456"
      ],
      "format": "password",
      "title": "Password",
      "type": "string",
      "writeOnly": true
    }
  },
  "required": [
    "age",
    "email",
    "name",
    "password"
  ],
  "title": "User",
  "type": "object"
}
"""

強制されていないField制約

Pydantic が強制されていない制約を見つけた場合、エラーが発生します。解析時にチェックされない場合でも、スキーマに制約を強制的に表示したい場合は、生のスキーマ属性名を指定して Field に可変引数を使用できます。

from pydantic import BaseModel, Field, PositiveInt

try:
    # this won't work since `PositiveInt` takes precedence over the
    # constraints defined in `Field`, meaning they're ignored
    class Model(BaseModel):
        foo: PositiveInt = Field(..., lt=10)

except ValueError as e:
    print(e)


# if you find yourself needing this, an alternative is to declare
# the constraints in `Field` (or you could use `conint()`)
# here both constraints will be enforced:
class ModelB(BaseModel):
    # Here both constraints will be applied and the schema
    # will be generated correctly
    foo: int = Field(..., gt=0, lt=10)


print(ModelB.model_json_schema())
"""
{
    'properties': {
        'foo': {
            'exclusiveMaximum': 10,
            'exclusiveMinimum': 0,
            'title': 'Foo',
            'type': 'integer',
        }
    },
    'required': ['foo'],
    'title': 'ModelB',
    'type': 'object',
}
"""

JSON スキーマの変更は、Field コンストラクターを介して、typing.Annotated を介して指定することもできます。

import json
from uuid import uuid4

from typing_extensions import Annotated

from pydantic import BaseModel, Field


class Foo(BaseModel):
    id: Annotated[str, Field(default_factory=lambda: uuid4().hex)]
    name: Annotated[str, Field(max_length=256)] = Field(
        'Bar', title='CustomName'
    )


print(json.dumps(Foo.model_json_schema(), indent=2))
"""
{
  "properties": {
    "id": {
      "title": "Id",
      "type": "string"
    },
    "name": {
      "default": "Bar",
      "maxLength": 256,
      "title": "CustomName",
      "type": "string"
    }
  },
  "title": "Foo",
  "type": "object"
}
"""

プログラムによるフィールドタイトルの生成

field_title_generatorパラメーターを使用すると、名前と情報に基づいてフィールドのタイトルをプログラムで生成できます。

次の例を参照してください。

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))
"""
{
  "properties": {
    "name": {
      "title": "NAME",
      "type": "string"
    },
    "age": {
      "title": "AGE",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

モデルレベルのカスタマイズ

model config を使用して、モデルの JSON スキーマ生成をカスタマイズすることもできます。具体的には、次の構成オプションが関連します。

json_schema_extraの使用

json_schema_extraオプションを使用すると、フィールド レベルまたはモデル レベルで JSON スキーマに追加情報を追加できます。 dictまたはCallable json_schema_extraに渡すことができます。

json_schema_extra dictとともに使用する

dict json_schema_extraに渡して、JSON スキーマに追加情報を追加できます。

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))
"""
{
  "examples": [
    {
      "a": "Foo"
    }
  ],
  "properties": {
    "a": {
      "title": "A",
      "type": "string"
    }
  },
  "required": [
    "a"
  ],
  "title": "Model",
  "type": "object"
}
"""

Callableでのjson_schema_extraの使用

Callablejson_schema_extraに渡して、関数を使用して JSON スキーマを変更できます。

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))
"""
{
  "properties": {
    "a": {
      "title": "A",
      "type": "integer"
    }
  },
  "title": "Model",
  "type": "object"
}
"""

json_schema_extraマージ

v2.9 以降、Pydantic は注釈付き型のjson_schema_extra辞書をマージします。このパターンは、以前のオーバーライド動作ではなく、マージに対してより追加的なアプローチを提供します。これは、複数のタイプにわたって json スキーマの追加情報を再利用する場合に非常に役立ちます。

この変更は、 BaseModelTypeAdapterインスタンスの間のjson_schema_extraマージ動作における意図しない差異が解決されるため、主にバグ修正であると考えられます。詳細については、この問題を参照してください。

import json

from typing_extensions import Annotated, TypeAlias

from pydantic import Field, TypeAdapter

ExternalType: TypeAlias = Annotated[
    int, Field(..., json_schema_extra={'key1': 'value1'})
]

ta = TypeAdapter(
    Annotated[ExternalType, Field(..., json_schema_extra={'key2': 'value2'})]
)
print(json.dumps(ta.json_schema(), indent=2))
"""
{
  "key1": "value1",
  "key2": "value2",
  "type": "integer"
}
"""

json_schema_extraの最後の仕様で以前の仕様をオーバーライドしたい場合は、 callable関数を使用して、キーの追加または削除、値の変更など、より重要な変更を加えることができます。 Pydantic v2.8 以前に存在するjson_schema_extraオーバーライドの動作を模倣したい場合は、このパターンを使用できます。

import json

from typing_extensions import Annotated, TypeAlias

from pydantic import Field, TypeAdapter
from pydantic.json_schema import JsonDict

ExternalType: TypeAlias = Annotated[
    int, Field(..., json_schema_extra={'key1': 'value1', 'key2': 'value2'})
]


def finalize_schema(s: JsonDict) -> None:
    s.pop('key1')
    s['key2'] = s['key2'] + '-final'
    s['key3'] = 'value3-final'


ta = TypeAdapter(
    Annotated[ExternalType, Field(..., json_schema_extra=finalize_schema)]
)
print(json.dumps(ta.json_schema(), indent=2))
"""
{
  "key2": "value2-final",
  "key3": "value3-final",
  "type": "integer"
}
"""

WithJsonSchemaアノテーション

??? API「APIドキュメント」 pydantic.json_schema.WithJsonSchema

!!!ヒント WithJsonSchema] の使用は、よりシンプルでエラーが発生しにくいため、カスタム型に__get_pydantic_json_schema__を実装するよりも推奨されます。

WithJsonSchema アノテーションを使用すると、型自体に__get_pydantic_core_schema__または__get_pydantic_json_schema__を実装することなく、特定の型に対して生成された (ベース) JSON スキーマをオーバーライドできます。

これにより、 Callableなどの JSON スキーマの生成時にエラーが発生する型、または is-instance コア スキーマを持つ型に対して、JSON スキーマを設定する方法が提供されます。

たとえば、次の例で [PlainValidator][pydantic.function_validators.PlainValidator] を使用すると、[PlainValidator][pydantic.function_validators.PlainValidator] はCallableであるため、JSON スキーマの生成時にエラーが発生します。ただし、 WithJsonSchema アノテーションを使用すると、カスタムMyInt型に対して生成された JSON スキーマをオーバーライドできます。

import json

from typing_extensions import Annotated

from pydantic import BaseModel, PlainValidator, WithJsonSchema

MyInt = Annotated[
    int,
    PlainValidator(lambda v: int(v) + 1),
    WithJsonSchema({'type': 'integer', 'examples': [1, 0, -1]}),
]


class Model(BaseModel):
    a: MyInt


print(Model(a='1').a)
#> 2

print(json.dumps(Model.model_json_schema(), indent=2))
"""
{
  "properties": {
    "a": {
      "examples": [
        1,
        0,
        -1
      ],
      "title": "A",
      "type": "integer"
    }
  },
  "required": [
    "a"
  ],
  "title": "Model",
  "type": "object"
}
"""

!!! noteこの問題で説明したように、将来、Pydantic は [PlainValidator][pydantic.function_validators.PlainValidator] のような型の JSON スキーマ生成の組み込みサポートを追加する可能性がありますが、WithJsonSchema アノテーション他のカスタム タイプにも引き続き役立ちます。

SkipJsonSchemaアノテーション

??? API「APIドキュメント」 pydantic.json_schema.SkipJsonSchema

SkipJsonSchema アノテーションを使用すると、生成された JSON スキーマから包含フィールド (またはフィールド仕様の一部) をスキップできます。詳細については、API ドキュメントを参照してください。

__get_pydantic_core_schema__の実装

カスタム タイプ ( field_name: TheTypeまたは field_name: Annotated[TheType, ...] )およびAnnotatedメタデータ(として使用されます) field_name: Annotated[int, SomeMetadata] ) __get_pydantic_core_schema__を実装することで、生成されたスキーマを変更またはオーバーライドできます。このメソッドは 2 つの位置引数を受け取ります。

  1. この型に対応する型アノテーション (つまり、 TheType[T][int]の場合はTheType[int]になります)。
  2. __get_pydantic_core_schema__の次の実装者を呼び出すハンドラー/コールバック。

ハンドラー システムはmode='wrap' validatorsと同じように機能します。この場合、入力は type で、出力はcore_schemaです。

生成されたcore_schema_をオーバーライドする_カスタム タイプの例を次に示します。

from dataclasses import dataclass
from typing import Any, Dict, List, Type

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())
"""
{
    'properties': {'value': {'title': 'Value', 'type': 'string'}},
    'required': ['value'],
    'title': 'MyModel',
    'type': 'object',
}
"""
print(MyModel(value='fox fox fox dog fox'))
"""
value = CompressedString(dictionary={0: 'fox', 1: 'dog'}, text=[0, 0, 0, 1, 0])
"""

print(MyModel(value='fox fox fox dog fox').model_dump(mode='json'))
#> {'value': 'fox fox fox dog fox'}

Pydantic はCompressedStringのスキーマを生成する方法を知らないため、その__get_pydantic_core_schema__メソッドでhandler(source)を呼び出すと、 pydantic.errors.PydanticSchemaGenerationError エラー。これはほとんどのカスタム タイプに当てはまります。そのため、カスタム タイプのhandlerを呼び出す必要はほとんどありません。

Annotatedメタデータのプロセスは、通常、 handlerを呼び出して Pydantic にスキーマの生成を処理させることができる点を除いて、ほとんど同じです。

from dataclasses import dataclass
from typing import Any, Sequence, Type

from pydantic_core import core_schema
from typing_extensions import Annotated

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
        )  # get the CoreSchema from the type / inner constraints
        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())
"""
{
    'properties': {'value': {'title': 'Value', 'type': 'string'}},
    'required': ['value'],
    'title': 'MyModel',
    'type': 'object',
}
"""
print(MyModel(value='CBA'))
#> value='CBA'

try:
    MyModel(value='XYZ')
except ValidationError as e:
    print(e)
    """
    1 validation error for MyModel
    value
      Value error, 'XYZ' is not restricted to 'ABC' [type=value_error, input_value='XYZ', input_type=str]
    """

ここまでスキーマをラップしてきましたが、スキーマを_変更する_だけ、または_無視し_たい場合は、それも可能です。

スキーマを変更するには、まずハンドラーを呼び出し、次に結果を変更します。

from typing import Any, Type

from pydantic_core import ValidationError, core_schema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class SmallString:
    def __get_pydantic_core_schema__(
        self,
        source: Type[Any],
        handler: GetCoreSchemaHandler,
    ) -> core_schema.CoreSchema:
        schema = handler(source)
        assert schema['type'] == 'str'
        schema['max_length'] = 10  # modify in place
        return schema


class MyModel(BaseModel):
    value: Annotated[str, SmallString()]


try:
    MyModel(value='too long!!!!!')
except ValidationError as e:
    print(e)
    """
    1 validation error for MyModel
    value
      String should have at most 10 characters [type=string_too_long, input_value='too long!!!!!', input_type=str]
    """

!!!ヒント スキーマをその場で変更するだけの場合でも、スキーマを返す_必要がある_ことに注意してください。

スキーマを完全にオーバーライドするには、ハンドラーを呼び出さずに独自のCoreSchema返します。

from typing import Any, Type

from pydantic_core import ValidationError, core_schema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class AllowAnySubclass:
    def __get_pydantic_core_schema__(
        self, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        # we can't call handler since it will fail for arbitrary types
        def validate(value: Any) -> Any:
            if not isinstance(value, source):
                raise ValueError(
                    f'Expected an instance of {source}, got an instance of {type(value)}'
                )

        return core_schema.no_info_plain_validator_function(validate)


class Foo:
    pass


class Model(BaseModel):
    f: Annotated[Foo, AllowAnySubclass()]


print(Model(f=Foo()))
#> f=None


class NotFoo:
    pass


try:
    Model(f=NotFoo())
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    f
      Value error, Expected an instance of <class '__main__.Foo'>, got an instance of <class '__main__.NotFoo'> [type=value_error, input_value=<__main__.NotFoo object at 0x0123456789ab>, input_type=NotFoo]
    """

上で見たように、 BaseModelタイプでフィールドに注釈を付けることは、生成された json スキーマを変更またはオーバーライドするために使用できます。ただし、 Annotatedを介してメタデータを保存することを利用したいが、生成された JSON スキーマをオーバーライドしたくない場合は、メタデータ クラスに実装された__get_pydantic_core_schema__の no-op バージョンを使用して次のアプローチを使用できます。

from typing import Type

from pydantic_core import CoreSchema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class Metadata(BaseModel):
    foo: str = 'metadata!'
    bar: int = 100

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source_type: Type[BaseModel], handler: GetCoreSchemaHandler
    ) -> CoreSchema:
        if cls is not source_type:
            return handler(source_type)
        return super().__get_pydantic_core_schema__(source_type, handler)


class Model(BaseModel):
    state: Annotated[int, Metadata()]


m = Model.model_validate({'state': 2})
print(repr(m))
#> Model(state=2)
print(m.model_fields)
"""
{
    'state': FieldInfo(
        annotation=int,
        required=True,
        metadata=[Metadata(foo='metadata!', bar=100)],
    )
}
"""

__get_pydantic_json_schema__の実装

__get_pydantic_json_schema__を実装して、生成された json スキーマを変更またはオーバーライドすることもできます。このメソッドの変更は JSON スキーマにのみ影響し、検証とシリアル化に使用されるコア スキーマには影響しません。

生成された JSON スキーマを変更する例を次に示します。

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))
"""
{
  "examples": [
    {
      "age": 25,
      "name": "John Doe"
    }
  ],
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "age": {
      "title": "Age",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

field_title_generatorの使用

field_title_generatorパラメーターを使用すると、名前と情報に基づいてフィールドのタイトルをプログラムで生成できます。これはフィールド レベルのfield_title_generatorに似ていますが、 ConfigDictオプションはクラスのすべてのフィールドに適用されます。

次の例を参照してください。

import json

from pydantic import BaseModel, ConfigDict


class Person(BaseModel):
    model_config = ConfigDict(
        field_title_generator=lambda field_name, field_info: field_name.upper()
    )
    name: str
    age: int


print(json.dumps(Person.model_json_schema(), indent=2))
"""
{
  "properties": {
    "name": {
      "title": "NAME",
      "type": "string"
    },
    "age": {
      "title": "AGE",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

model_title_generatorの使用

model_title_generator構成オプションはfield_title_generatorオプションに似ていますが、モデル自体のタイトルに適用され、モデル クラスを入力として受け入れます。

次の例を参照してください。

import json
from typing import Type

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))
"""
{
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "age": {
      "title": "Age",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Title-Person",
  "type": "object"
}
"""

JSON スキーマの種類

タイプ、カスタム フィールド タイプ、および制約 ( max_lengthなど) は、次の優先順位で対応する仕様形式にマップされます (同等の形式が利用可能な場合)。

  1. JSONスキーマコア
  2. JSON スキーマの検証
  3. OpenAPI データ型
  4. 標準format JSON フィールドは、より複雑なstringサブタイプの Pydantic 拡張機能を定義するために使用されます。

Python または Pydantic から JSON スキーマへのフィールド スキーマ マッピングは次のように行われます。

トップレベルのスキーマの生成

モデルと関連するリストのみを含むトップレベルの JSON スキーマを生成することもできます。 ` $defs` 内のサブモデル: ```py Output="json" 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 クラス 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)) """ { "$ defs": { "バー": { "プロパティ": { "c": { "タイトル": "C", "型": "整数" } }, "必須": [ 「c」 ]、 "title": "バー", "タイプ": "オブジェクト" }, 「フー」: { "プロパティ": { "a": { 「デフォルト」: null、 "タイトル": "A", "タイプ": "文字列" } }, "タイトル": "フー", "タイプ": "オブジェクト" }, "モデル": { "プロパティ": { "b": { 」 $defs": { "FooBar": { "properties": { "count": { "title": "Count", "type": "integer" }, "size": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Size" } }, "required": [ "count" ], "title": "FooBar", "type": "object" }, "Gender": { "enum": [ "male", "female", "other", "not_given" ], "title": "Gender", "type": "string" } }, "description": "This is the description of the main model", "properties": { "foo_bar": { "$ デフ/フー」 } }, "必須": [ 「b」 ]、 "タイトル": "モデル", "タイプ": "オブジェクト" } }, "title": "私のスキーマ" } 「」

## Customizing the JSON Schema Generation Process

??? api "API Documentation"
    [`pydantic.json_schema`][pydantic.json_schema.GenerateJsonSchema]<br>

If you need custom schema generation, you can use a `schema_generator`, modifying the
[`GenerateJsonSchema`][pydantic.json_schema.GenerateJsonSchema] class as necessary for your application.

The various methods that can be used to produce JSON schema accept a keyword argument `schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema`, and you can pass your custom subclass to these methods in order to use your own approach to generating JSON schema.

`GenerateJsonSchema` implements the translation of a type's `pydantic-core` schema into a JSON schema.
By design, this class breaks the JSON schema generation process into smaller methods that can be easily overridden in
subclasses to modify the "global" approach to generating JSON schema.

```

py 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))
"""
{
    'properties': {'x': {'title': 'X', 'type': 'integer'}},
    'required': ['x'],
    'title': 'Customize title',
    'type': 'object',
    '$schema': 'https://json-schema.org/draft/2020-12/schema',
}
"""

以下は、有効な JSON スキーマを持たないフィールドをスキーマから除外するために使用できるアプローチです。

from typing 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)
"""
{
    'properties': {
        'name': {'default': 'example', 'title': 'Name', 'type': 'string'}
    },
    'title': 'Example',
    'type': 'object',
}
"""

カスタマイズする $ref`s in JSON Schema The format of `$ref model_json_schema() を呼び出すことで変更できます。

または model_dump_json()ref_templateキーワード引数とともに使用します。定義は常にキーの下に保存されます。 $defs`, but a specified prefix can be used for the references. This is useful if you need to extend or modify the JSON schema default definitions location. For example, with OpenAPI: ```py output="json" 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, ) ) """ { "$defs": { "Foo": { "properties": { "a": { "title": "A", "type": "integer" } }, "required": [ "a" ], "title": "Foo", "type": "object" } }, "properties": { "a": { "$ref": "#/components/schemas/Foo" } }, "required": [ "a" ], "title": "Model", "type": "object" } """ ``` ## Miscellaneous Notes on JSON Schema Generation * The JSON schema for `Optional` fields indicates that the value `null` is allowed. * The `Decimal` type is exposed in JSON schema (and serialized) as a string. * Since the `namedtuple` type doesn't exist in JSON, a model's JSON schema does not preserve `namedtuple`s as `namedtuple`s. * Sub-models used are added to the `$defs 仕様に従って、JSON 属性と参照が行われます。

  • カスタム タイトル、説明、デフォルト値などの変更が ( Fieldクラス経由で) 行われたサブモデルは、参照されるのではなく再帰的に組み込まれます。
  • モデルのdescription 、クラスの docstring またはFieldクラスの引数のdescriptionから取得されます。
  • スキーマはデフォルトでエイリアスをキーとして使用して生成されますが、代わりに model_json_schema() または model_dump_json() を呼び出すことでモデル プロパティ名を使用して生成できます。 model_dump_json] をby_alias=Falseキーワード引数とともに使用します。
Was this page helpful?