杰瑞科技汇

Python如何定义或校验JSONSchema格式?

JSON Schema 本身不是一个 Python 库,而是一个规范,用于描述和验证 JSON 数据的结构,你可以把它想象成 JSON 数据的“蓝图”或“数据类型检查器”。

Python如何定义或校验JSONSchema格式?-图1
(图片来源网络,侵删)

在 Python 中,我们通常使用一个叫做 jsonschema 的库来处理这个规范。


安装 jsonschema

你需要安装这个库,可以使用 pip:

pip install jsonschema

JSON Schema 的基本概念和常用关键字

在写 Python 代码之前,我们先要了解 JSON Schema 的基本构成,一个 Schema 本身也是一个 JSON 对象,它由一系列的关键字组成,用来描述你的数据应该是什么样的。

关键字 描述 示例
type 定义数据的类型。 "string", "number", "integer", "boolean", "array", "object", "null"
properties (用于 object) 定义对象有哪些属性,以及每个属性的 Schema。 { "name": { "type": "string" } }
required (用于 object) 指定对象中哪些属性是必须的。 ["name", "age"]
items (用于 array) 定义数组中每个元素必须遵守的 Schema。 { "type": "string" }
minItems / maxItems (用于 array) 定义数组元素的最小/最大数量。 "minItems": 1
pattern (用于 string) 定义字符串必须匹配的正则表达式。 "pattern": "^\\d+$" (匹配纯数字)
minimum / maximum (用于 numberinteger) 定义数值的最小/最大值(不包含)。 "minimum": 0
minimum / maximum (用于 numberinteger) 定义数值的最小/最大值(包含)。 "minimum": 0
enum 定义值必须是给定的枚举值之一。 "enum": ["red", "green", "blue"]
$schema 指定所使用的 JSON Schema 的版本,推荐使用最新版本。 "$schema": "http://json-schema.org/draft-07/schema#"

Python 中使用 jsonschema 的核心步骤

使用 jsonschema 库进行验证非常简单,主要分为三步:

Python如何定义或校验JSONSchema格式?-图2
(图片来源网络,侵删)
  1. 定义你的 JSON Schema:一个 Python 字典,描述了有效数据的结构。
  2. 准备要验证的数据:一个 Python 字典,代表你想要检查的 JSON 数据。
  3. 调用 validate() 函数:将数据和 Schema 传递给 jsonschema.validate(),如果数据有效,函数正常返回;如果无效,则会抛出 jsonschema.exceptions.ValidationError 异常。

完整代码示例

下面我们通过一个具体的例子来演示。

场景:我们想要验证一个“用户”对象,一个有效的用户应该包含:

  • name: 字符串,且是必须的。
  • age: 整数,必须在 18 到 120 之间(包含),且是必须的。
  • email: 字符串,必须符合邮箱格式(简化版,使用正则),是必须的。
  • is_active: 布尔值,可选。
  • skills: 字符串数组,至少包含一项技能。

步骤 1 & 2: 定义 Schema 和待验证数据

# 1. 定义 JSON Schema (一个 Python 字典)
user_schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",  # 指定 schema 版本: "User Schema",
    "description": "A user object",
    "type": "object",
    "properties": {
        "name": {
            "description": "The user's full name",
            "type": "string"
        },
        "age": {
            "description": "The user's age in years",
            "type": "integer",
            "minimum": 18,
            "maximum": 120
        },
        "email": {
            "description": "The user's email address",
            "type": "string",
            "pattern": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" # 简单的邮箱正则
        },
        "is_active": {
            "description": "Whether the user account is active",
            "type": "boolean"
        },
        "skills": {
            "description": "A list of the user's skills",
            "type": "array",
            "items": {
                "type": "string"
            },
            "minItems": 1
        }
    },
    "required": ["name", "age", "email", "skills"]  # 指定必须的字段
}
# 2. 准备一些要验证的数据
# --- 这是一个有效的用户 ---
valid_user = {
    "name": "John Doe",
    "age": 30,
    "email": "john.doe@example.com",
    "is_active": True,
    "skills": ["Python", "Django"]
}
# --- 这是一个无效的用户(缺少 age,skills 为空数组)---
invalid_user_1 = {
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "is_active": False,
    "skills": []  # 违反了 minItems: 1
}
# --- 另一个无效的用户(age 超出范围,email 格式错误)---
invalid_user_2 = {
    "name": "Peter Jones",
    "age": 150, # 违反了 maximum: 120
    "email": "peter.jones@", # 违反了 pattern
    "skills": ["Java", "Spring"]
}

步骤 3: 执行验证

from jsonschema import validate
from jsonschema.exceptions import ValidationError
def validate_user(data, schema):
    """
    验证用户数据,并打印结果。
    """
    print(f"\n--- 正在验证数据: {data} ---")
    try:
        validate(instance=data, schema=schema)
        print("✅ 验证通过!数据是有效的。")
    except ValidationError as e:
        print(f"❌ 验证失败!错误信息: {e.message}")
        # 你可以打印更多关于错误的信息
        print(f"   错误路径: {' -> '.join(map(str, e.path))}")
        print(f"   无效值: {e.instance}")
# --- 执行验证 ---
print("--- 开始验证 ---")
validate_user(valid_user, user_schema)
validate_user(invalid_user_1, user_schema)
validate_user(invalid_user_2, user_schema)

运行结果

--- 开始验证 ---
--- 正在验证数据: {'name': 'John Doe', 'age': 30, 'email': 'john.doe@example.com', 'is_active': True, 'skills': ['Python', 'Django']} ---
✅ 验证通过!数据是有效的。
--- 正在验证数据: {'name': 'Jane Doe', 'email': 'jane.doe@example.com', 'is_active': False, 'skills': []} ---
❌ 验证失败!错误信息: [] is too short
   错误路径: skills
   无效值: []
--- 正在验证数据: {'name': 'Peter Jones', 'age': 150, 'email': 'peter.jones@', 'skills': ['Java', 'Spring']} ---
❌ 验证失败!错误信息: 'peter.jones@' does not match '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
   错误路径: email
   无效值: peter.jones@

高级用法

a. 自定义错误消息

jsonschema 默认的错误消息很清晰,但如果你想自定义,可以使用 format 关键字(仅适用于字符串)或者通过捕获异常后修改程序逻辑来实现。

# 使用 format 提供更友好的描述
user_schema_with_format = {
    # ... (其他部分保持不变)
    "properties": {
        "email": {
            "type": "string",
            "format": "email", # 'email' 是一个预定义的格式
            "errorMessage": "请提供一个有效的电子邮箱地址。"
        }
    }
}
# 注意:直接使用 format: "email" 可能需要更高级的库(如 `jsonschema-with-format-nongpl`)
# 来完全支持,这里仅为演示,自定义错误通常在异常处理逻辑中完成。

b. 复杂 Schema: allOf, anyOf, oneOf, not

当需要组合多个条件时,这些关键字非常有用。

Python如何定义或校验JSONSchema格式?-图3
(图片来源网络,侵删)
  • allOf: 必须同时满足所有子 Schema。
  • anyOf: 必须满足至少一个子 Schema。
  • oneOf: 必须且只能满足一个子 Schema。
  • not: 不能满足给定的 Schema。

示例: 一个数字,它要么是 null,要么是一个大于 0 的整数。

import jsonschema
# 定义一个 Schema,要求值是 null 或者一个正整数
nullable_positive_integer_schema = {
    "anyOf": [
        { "type": "null" },
        {
            "type": "integer",
            "minimum": 1
        }
    ]
}
# 测试数据
data1 = None
data2 = 42
data3 = -5
data4 = "hello"
print("\n--- 测试复杂 Schema ---")
try:
    validate(instance=data1, schema=nullable_positive_integer_schema)
    print(f"'{data1}' 是有效的。")
except ValidationError:
    print(f"'{data1}' 是无效的。")
try:
    validate(instance=data2, schema=nullable_positive_integer_schema)
    print(f"'{data2}' 是有效的。")
except ValidationError:
    print(f"'{data2}' 是无效的。")
try:
    validate(instance=data3, schema=nullable_positive_integer_schema)
    print(f"'{data3}' 是有效的。")
except ValidationError:
    print(f"'{data3}' 是无效的。")
try:
    validate(instance=data4, schema=nullable_positive_integer_schema)
    print(f"'{data4}' 是有效的。")
except ValidationError:
    print(f"'{data4}' 是无效的。")

c. 生成 JSON Schema (从 Python 类)

如果你使用像 Pydantic 这样的现代数据验证库,可以非常方便地从 Python 类生成 JSON Schema,这极大地简化了 Schema 的创建过程。

示例使用 Pydantic:

首先安装 Pydantic: pip install pydantic

from pydantic import BaseModel, EmailStr, constr, validator
from typing import List
class UserModel(BaseModel):
    name: constr(min_length=1)  # 至少一个字符的字符串
    age: int
    email: EmailStr
    is_active: bool = True  # 有默认值,所以不是必须的
    skills: List[str]
    # Pydantic 会自动生成符合 JSON Schema 的描述
    # 你也可以添加自定义验证器
    @validator('age')
    def age_must_be_adult(cls, v):
        if v < 18:
            raise ValueError("用户必须年满18岁")
        return v
# 从 Pydantic 模型生成 JSON Schema
user_schema_from_pydantic = UserModel.model_json_schema()
print("\n--- 由 Pydantic 生成的 JSON Schema ---")
import json
print(json.dumps(user_schema_from_pydantic, indent=2))
# 现在这个 schema 可以和 jsonschema 库一起使用
from jsonschema import validate
valid_pydantic_user = {"name": "Alice", "age": 25, "email": "alice@test.com", "skills": ["fast"]}
validate(instance=valid_pydantic_user, schema=user_schema_from_pydantic)
print("\n✅ Pydantic 生成的 Schema 验证通过!")

特性 描述
核心库 jsonschema
工作流程 定义 Schema -> 准备数据 -> 调用 validate() -> 处理异常
优点 标准化:JSON Schema 是一个业界标准,跨语言通用。
强大灵活:支持复杂的数据结构、嵌套、条件组合等。
自动化:可以集成到 CI/CD 流程中,确保 API 数据的正确性。
文档化:Schema 本身就是一份清晰的数据结构文档。
适用场景 API 请求/响应数据验证、配置文件验证、数据清洗、确保数据一致性等。

希望这份详细的指南能帮助你掌握在 Python 中使用 JSON Schema!

分享:
扫描分享到社交APP
上一篇
下一篇