
JSON Schema:验证并文档化你的 JSON 数据
JSON Schema 是一种用于注解和验证 JSON 文档的词汇表。它描述了数据的结构——哪些字段是必需的,它们必须是什么类型,以及必须满足哪些约束。可以把它看作是运行时数据的 TypeScript。
JSON Schema 用于:
- 验证 API 请求/响应体
- 在 OpenAPI 规范中文档化数据结构
- 根据 schema 定义生成表单
- 验证配置文件
- 代码生成(从 schema 生成 TypeScript 类型)
基本 Schema 结构
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/user.json",
"title": "User",
"description": "A registered user in the system",
"type": "object"
}
| 关键字 | 用途 |
|---|---|
$schema |
指定使用的 JSON Schema 草案版本 |
$id |
该 schema 的唯一标识符 |
title |
人类可读的名称 |
description |
文档说明 |
type |
数据类型约束 |
基本类型
{ "type": "string" }
{ "type": "number" }
{ "type": "integer" }
{ "type": "boolean" }
{ "type": "null" }
{ "type": ["string", "null"] }
字符串约束
{
"type": "string",
"minLength": 1,
"maxLength": 100,
"pattern": "^[a-zA-Z0-9_]+quot;,
"format": "email"
}
Format 值(验证取决于验证器库):
| Format | 验证内容 |
|---|---|
"email" |
电子邮件地址 |
"uri" |
URI |
"date" |
YYYY-MM-DD |
"time" |
HH:MM:SS |
"date-time" |
ISO 8601 日期时间 |
"uuid" |
UUID v1–v5 |
"ipv4" |
IPv4 地址 |
"ipv6" |
IPv6 地址 |
"hostname" |
DNS 主机名 |
数字约束
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": 0,
"multipleOf": 0.01
}
对象
{
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "Unique user ID"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"format": "email"
},
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"]
},
"createdAt": {
"type": "string",
"format": "date-time"
}
},
"required": ["id", "name", "email"],
"additionalProperties": false
}
关键关键字:
properties:定义每个键的 schemarequired:必须存在的键的数组additionalProperties: false:拒绝不在properties中的额外字段additionalProperties: { "type": "string" }:允许额外字段但约束其类型
属性数量
{
"type": "object",
"minProperties": 1,
"maxProperties": 10
}
数组
{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 100,
"uniqueItems": true
}
元组验证(固定长度数组)
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" },
{ "type": "boolean" }
],
"items": false
}
这会验证 ["Alice", 30, true],但拒绝 ["Alice", 30, true, "extra"]。
嵌套 Schema
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
},
"required": ["street", "city", "country"]
}
},
"required": ["name"]
},
"tags": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true
}
}
}
使用 $ref 重用 Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"Address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
},
"required": ["street", "city"]
},
"User": {
"type": "object",
"properties": {
"name": { "type": "string" },
"homeAddress": { "$ref": "#/$defs/Address" },
"workAddress": { "$ref": "#/$defs/Address" }
}
}
},
"$ref": "#/$defs/User"
}
$defs(以前称为 definitions)保存可重用的子 schema。$ref 通过 JSON Pointer 引用它们。
组合 Schema
// oneOf:恰好一个匹配
{
"oneOf": [
{ "type": "string" },
{ "type": "integer" }
]
}
// anyOf:一个或多个匹配
{
"anyOf": [
{ "type": "string", "format": "email" },
{ "type": "string", "format": "uri" }
]
}
// allOf:全部匹配(用于组合/继承)
{
"allOf": [
{ "$ref": "#/$defs/BaseUser" },
{
"properties": {
"adminLevel": { "type": "integer", "minimum": 1, "maximum": 3 }
},
"required": ["adminLevel"]
}
]
}
// not:必须不匹配
{
"not": { "type": "string" }
}
条件验证(if/then/else)
{
"type": "object",
"properties": {
"type": { "type": "string", "enum": ["individual", "business"] },
"name": { "type": "string" },
"companyName": { "type": "string" },
"vatNumber": { "type": "string" }
},
"if": {
"properties": { "type": { "const": "business" } }
},
"then": {
"required": ["companyName", "vatNumber"]
},
"else": {
"required": ["name"]
}
}
在 JavaScript 中验证
AJV(推荐)
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({ allErrors: true });
addFormats(ajv); // 添加 email, date-time, uri 等格式
const schema = {
type: 'object',
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0, maximum: 150 },
},
required: ['name', 'email'],
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { name: 'Alice', email: 'alice@example.com', age: 30 };
const valid = validate(data);
if (!valid) {
console.log(validate.errors);
// [{
// instancePath: '/email',
// message: 'must match format "email"',
// ...
// }]
}
Zod(TypeScript 优先,现代应用中更常见)
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['admin', 'editor', 'viewer']),
});
type User = z.infer<typeof UserSchema>; // 推导 TypeScript 类型
const result = UserSchema.safeParse({ name: 'Alice', email: 'bad-email', role: 'admin' });
if (!result.success) {
result.error.issues.forEach(issue => {
console.log(issue.path, issue.message);
// ['email'] 'Invalid email'
});
}
从 Zod 生成 JSON Schema
import { zodToJsonSchema } from 'zod-to-json-schema';
const jsonSchema = zodToJsonSchema(UserSchema, 'User');
// 从 Zod 定义生成标准 JSON Schema
OpenAPI 中的 JSON Schema
# openapi.yaml
paths:
/users:
post:
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserInput'
components:
schemas:
CreateUserInput:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
required: [name, email]
additionalProperties: false
→ 在浏览器中使用 JSON Viewer 探索和验证 JSON 数据结构。