Pydantic中json_or_python_schema的json_schema属性用途及调用时机
问题背景
存在无法修改的外部类OriginalExternalClass,需要将其作为继承自Pydantic BaseModel的PydanticClassWithExternalClassMember类的字段。为规避使用arbitrary_types flag并实现该字段的类型校验与序列化能力,编写了如下代码:
from typing import Any from pydantic_core import core_schema import pydantic class OriginalExternalClass: def __init__(self, a: int, b: str) -> None: self.a = a self.b = b class PydanticExternalClass(OriginalExternalClass): @classmethod def __get_pydantic_core_schema__( cls, _source_type: Any, _handler: pydantic.GetCoreSchemaHandler, ) -> core_schema.CoreSchema: def json_schema(value: dict) -> OriginalExternalClass: # NEVER CALLED!!! print("json_schema called") result = OriginalExternalClass(**value) return result def python_schema( value: OriginalExternalClass, ) -> OriginalExternalClass: print("python_schema called") if isinstance(value, dict): value = OriginalExternalClass(**value) if not isinstance(value, OriginalExternalClass): raise pydantic.ValidationError( msg="Expected a `ExternalClass` instance", loc=("third_party_type",), type=OriginalExternalClass, ) return value def serialization(value: OriginalExternalClass) -> dict: print("serialization called") return {"a": value.a, "b": value.b} return core_schema.json_or_python_schema( json_schema=core_schema.no_info_plain_validator_function(json_schema), python_schema=core_schema.no_info_plain_validator_function(python_schema), serialization=core_schema.plain_serializer_function_ser_schema( serialization ), ) class PydanticClassWithExternalClassMember(pydantic.BaseModel): x: PydanticExternalClass
测试情况
执行以下测试代码时,发现json_schema函数从未被调用:
print( "m1 = PydanticClassWithExternalClassMember(x=PydanticExternalClass(1, 'basgsadf'))" ) m1 = PydanticClassWithExternalClassMember(x=PydanticExternalClass(1, "basgsadf")) print("d = m1.model_dump()") d = m1.model_dump() print("m1 = PydanticClassWithExternalClassMember(**d)") m1 = PydanticClassWithExternalClassMember(**d)
输出结果:
m1 = PydanticClassWithExternalClassMember(x=PydanticExternalClass(1, 'basgsadf')) python_schema called d = m1.model_dump() serialization called m1 = PydanticClassWithExternalClassMember(**d) python_schema called
核心疑问
需要明确:
core_schema.json_or_python_schema的json_schema属性的用途是什么?- 该属性对应的函数何时会被调用?
解答
1. json_schema的用途
json_schema是json_or_python_schema专门用于处理JSON兼容原生数据的校验与转换逻辑,负责将JSON解析后的纯字典、字符串、数字等原生类型,转换为目标Python对象(这里是OriginalExternalClass实例)。它和python_schema分工明确:python_schema处理已有的Python对象(包括字典形式的Python参数),json_schema则专注于JSON反序列化场景的输入。
2. json_schema的触发时机
json_schema会在以下场景中被调用:
- 使用
model_validate_json()方法直接传入JSON字符串时:此时输入是纯JSON格式的数据,Pydantic会优先走json_schema路径处理 - 当通过
model_validate()传入JSON兼容字典,且明确指定from_attributes=False(默认值),同时Pydantic判定该场景属于JSON反序列化时
为什么测试代码中未触发json_schema
测试中使用PydanticClassWithExternalClassMember(**d)时,字典d被当作Python关键字参数传入,Pydantic会将其视为Python对象场景,走python_schema逻辑。同时你的python_schema函数中已经包含了处理字典的逻辑,所以即使传入字典也不会触发json_schema。
触发json_schema的示例
修改测试代码,使用model_validate_json()验证JSON字符串,即可触发json_schema:
import json # 序列化得到JSON字符串 json_str = json.dumps(d) # 用JSON字符串验证模型 m2 = PydanticClassWithExternalClassMember.model_validate_json(json_str)
此时输出会包含:
json_schema called
内容的提问来源于stack exchange,提问作者newandlost

