跳转到内容

JSON

Json 解析

API 文档

pydantic.main.BaseModel.model_validate_json pydantic.type_adapter.TypeAdapter.validate_json pydantic_core.from_json

Pydantic 提供了内置的 JSON 解析功能,这有助于实现:

  • 显著的性能提升,而无需使用第三方库
  • 支持自定义错误
  • 支持 strict (严格) 规范

以下是一个通过 model_validate_json 方法使用 Pydantic 内置 JSON 解析的示例,展示了在解析与模型类型注解不匹配的 JSON 数据时对 strict 规范的支持:

from datetime import date

from pydantic import BaseModel, ConfigDict, ValidationError


class Event(BaseModel):
    model_config = ConfigDict(strict=True)

    when: date
    where: tuple[int, int]


json_data = '{"when": "1987-01-28", "where": [51, -1]}'
print(Event.model_validate_json(json_data))  # (1)!
#> when=datetime.date(1987, 1, 28) where=(51, -1)

try:
    Event.model_validate({'when': '1987-01-28', 'where': [51, -1]})  # (2)!
except ValidationError as e:
    print(e)
    """
    2 validation errors for Event
    when
      Input should be a valid date [type=date_type, input_value='1987-01-28', input_type=str]
    where
      Input should be a valid tuple [type=tuple_type, input_value=[51, -1], input_type=list]
    """
  1. JSON 没有 date 或元组类型,但 Pydantic 知道这一点,因此在直接解析 JSON 时,允许分别使用字符串和数组作为输入。
  2. 如果您将相同的值传递给 model_validate 方法,Pydantic 将会引发一个验证错误,因为启用了 strict 配置。

在 v2.5.0 及以上版本中,Pydantic 使用 jiter,一个快速且可迭代的 JSON 解析器,来解析 JSON 数据。与 serde 相比,使用 jiter 带来了适度的性能提升,并且未来还会进一步优化。

jiter JSON 解析器与 serde JSON 解析器几乎完全兼容,一个显著的增强是 jiter 支持反序列化 infNaN 值。未来,jiter 旨在支持验证错误时能包含原始 JSON 输入中无效值的位置信息。

部分 JSON 解析

从 v2.7.0 开始,Pydantic 的 JSON 解析器 支持部分 JSON 解析,该功能通过 pydantic_core.from_json 提供。以下是该功能的一个示例:

from pydantic_core import from_json

partial_json_data = '["aa", "bb", "c'  # (1)!

try:
    result = from_json(partial_json_data, allow_partial=False)
except ValueError as e:
    print(e)  # (2)!
    #> EOF while parsing a string at line 1 column 15

result = from_json(partial_json_data, allow_partial=True)
print(result)  # (3)!
#> ['aa', 'bb']
  1. 这个 JSON 列表是不完整的 - 它缺少一个闭合的 "]
  2. allow_partial 设置为 False (默认值) 时,会发生解析错误。
  3. allow_partial 设置为 True 时,部分输入被成功反序列化。

这也适用于反序列化部分字典。例如:

from pydantic_core import from_json

partial_dog_json = '{"breed": "lab", "name": "fluffy", "friends": ["buddy", "spot", "rufus"], "age'
dog_dict = from_json(partial_dog_json, allow_partial=True)
print(dog_dict)
#> {'breed': 'lab', 'name': 'fluffy', 'friends': ['buddy', 'spot', 'rufus']}

验证 LLM 输出

此功能对于验证 LLM (大语言模型) 的输出特别有用。我们写了一些关于这个主题的博客文章,您可以在我们的网站上找到。

在 Pydantic 的未来版本中,我们期望通过 Pydantic 的其他 JSON 验证函数 (pydantic.main.BaseModel.model_validate_jsonpydantic.type_adapter.TypeAdapter.validate_json) 或模型配置来扩展对此功能的支持。敬请期待 🚀!

目前,您可以结合使用 pydantic_core.from_jsonpydantic.main.BaseModel.model_validate 来达到相同的结果。以下是一个示例:

from pydantic_core import from_json

from pydantic import BaseModel


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


partial_dog_json = '{"breed": "lab", "name": "fluffy", "friends": ["buddy", "spot", "rufus"], "age'
dog = Dog.model_validate(from_json(partial_dog_json, allow_partial=True))
print(repr(dog))
#> Dog(breed='lab', name='fluffy', friends=['buddy', 'spot', 'rufus'])

提示

为了使部分 JSON 解析可靠地工作,模型上的所有字段都应该有默认值。

查看以下示例,更深入地了解如何将默认值与部分 JSON 解析结合使用:

将默认值与部分 JSON 解析结合使用

from typing import Annotated, Any, Optional

import pydantic_core

from pydantic import BaseModel, ValidationError, WrapValidator


def default_on_error(v, handler) -> Any:
    """
    Raise a PydanticUseDefault exception if the value is missing.

    This is useful for avoiding errors from partial
    JSON preventing successful validation.
    """
    try:
        return handler(v)
    except ValidationError as exc:
        # there might be other types of errors resulting from partial JSON parsing
        # that you allow here, feel free to customize as needed
        if all(e['type'] == 'missing' for e in exc.errors()):
            raise pydantic_core.PydanticUseDefault()
        else:
            raise


class NestedModel(BaseModel):
    x: int
    y: str


class MyModel(BaseModel):
    foo: Optional[str] = None
    bar: Annotated[
        Optional[tuple[str, int]], WrapValidator(default_on_error)
    ] = None
    nested: Annotated[
        Optional[NestedModel], WrapValidator(default_on_error)
    ] = None


m = MyModel.model_validate(
    pydantic_core.from_json('{"foo": "x", "bar": ["world",', allow_partial=True)
)
print(repr(m))
#> MyModel(foo='x', bar=None, nested=None)


m = MyModel.model_validate(
    pydantic_core.from_json(
        '{"foo": "x", "bar": ["world", 1], "nested": {"x":', allow_partial=True
    )
)
print(repr(m))
#> MyModel(foo='x', bar=('world', 1), nested=None)

缓存字符串

从 v2.7.0 开始,Pydantic 的 JSON 解析器 支持配置在 JSON 解析和验证期间如何缓存 Python 字符串(例如,在 Python 验证期间,当从 Rust 字符串构造 Python 字符串时,如 strip_whitespace=True 之后)。cache_strings 设置通过模型配置pydantic_core.from_json 提供。

cache_strings 设置可以接受以下任何值:

  • True'all' (默认值):缓存所有字符串
  • 'keys':仅缓存字典键,这适用于与 pydantic_core.from_json 一起使用或使用 Json 解析 JSON 时
  • False'none':不缓存

使用字符串缓存功能可以提升性能,但会略微增加内存使用量。

字符串缓存详情

  1. 字符串使用一个大小为 16,384 的全相联缓存进行缓存。
  2. 只有 len(string) < 64 的字符串才会被缓存。
  3. 查找缓存存在一些开销,但这通常是为了避免构造字符串而值得的。然而,如果您知道数据中重复的字符串会很少,通过设置 cache_strings=False 禁用此功能可能会带来性能提升。

JSON 序列化

API 文档

pydantic.main.BaseModel.model_dump_json
pydantic.type_adapter.TypeAdapter.dump_json
pydantic_core.to_json

有关 JSON 序列化的更多信息,请参阅序列化概念页面。