跳到内容

JSON

Json 解析

API 文档

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

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

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

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

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 序列化的更多信息,请参阅序列化概念页面。