如何使用Pydantic验证包含内部JSON字符串的JSON数据?
问题说明
API接收到的JSON字符串中,inner_data字段是JSON格式的字符串(而非直接的JSON对象):
{"data": 123, "inner_data": "{\"color\": \"RED\"}"}
按常规方式定义Pydantic模型(将inner_data声明为InnerData类型)后,调用model_validate_json会触发验证错误:
pydantic_core._pydantic_core.ValidationError: 1 validation error for Expected inner_data Input should be an object [type=model_type, input_value='{"color": "RED"}', input_type=str]
原因是Pydantic默认期望inner_data是JSON对象,而非字符串类型的JSON结构。
解决方案
以下三种方法可解决该问题:
方法1:使用前置验证器解析JSON字符串
通过BeforeValidator添加前置处理逻辑,先将inner_data的字符串解析为JSON对象,再交由InnerData模型验证:
from pydantic import BaseModel, BeforeValidator from typing import Any import json def parse_json_str(v: Any) -> Any: if isinstance(v, str): return json.loads(v) return v class InnerData(BaseModel): color: str class Expected(BaseModel): data: int inner_data: InnerData = BeforeValidator(parse_json_str) # 验证示例 incoming_json_string = '{"data": 123, "inner_data": "{\"color\": \"RED\"}"}' expected = Expected.model_validate_json(incoming_json_string)
方法2:封装解析逻辑到内层模型
自定义模型的核心验证逻辑,让InnerData自动处理字符串类型的输入:
from pydantic import BaseModel, GetJsonSchemaHandler from pydantic_core import core_schema from typing import Any, Type import json class JsonStringParsableModel(BaseModel): @classmethod def __get_pydantic_core_schema__(cls, source_type: Type[Any], handler: GetJsonSchemaHandler) -> core_schema.CoreSchema: def validate(value: Any) -> Any: if isinstance(value, str): value = json.loads(value) return cls(**value) return core_schema.no_info_plain_validator_function( validate, json_schema=handler(cls), ) class InnerData(JsonStringParsableModel): color: str class Expected(BaseModel): data: int inner_data: InnerData # 验证示例 incoming_json_string = '{"data": 123, "inner_data": "{\"color\": \"RED\"}"}' expected = Expected.model_validate_json(incoming_json_string)
方法3:手动分步解析JSON
先解析外层JSON,再手动处理inner_data字段的字符串解析,最后传入模型验证:
import json from pydantic import BaseModel class InnerData(BaseModel): color: str class Expected(BaseModel): data: int inner_data: InnerData # 验证示例 incoming_json_string = '{"data": 123, "inner_data": "{\"color\": \"RED\"}"}' outer_dict = json.loads(incoming_json_string) outer_dict["inner_data"] = json.loads(outer_dict["inner_data"]) expected = Expected(**outer_dict)
内容的提问来源于stack exchange,提问作者user1367204
相关产品推荐
相关产品推荐

