信息的自解释性越强,那么它将越通用。
30年前的XML和10年前的JSON能非常精确表达信息的结构和关系,所以能成为最通用的跨系统数据交换的标准,大多数人日用而不自知(我便是其中之一)。可扩展性是软件开发中绕不开的话题,为此,研发团队没少在这个方面投入心思。作为Web开发的重要标准JSON,本身就具有极强的可扩展性,然而在如何定义和约束JSON方面团队却闷着头造轮子,摆在那里十几年确未曾正眼看过,着实是我的疏忽。写这篇文章旨在系统地梳理JSON Schema
要点,为将来的迭代改进作为参考依据。
0. JSON Schema 背景
以下是JSON Schema发展史的简要表格梳理:
发展阶段 | 时间范围 | 核心特点 | 重要版本/事件 |
---|---|---|---|
早期探索 | 2007-2010 | 社区驱动,解决JSON数据格式一致性问题,初步提出基础类型和约束概念 | 首个公开提案出现;早期草案包含type 、required 等基础关键字 |
规范成型 | 2011-2013 | 以"Draft"形式统一语法,明确"用JSON描述JSON"的设计理念,奠定基础框架 | Draft 01(引入$schema );Draft 04(首个广泛认可的稳定版本,完善$ref 等) |
功能完善 | 2014-2019 | 扩展复杂场景验证能力,支持条件逻辑、格式校验,被主流技术生态接纳 | Draft 06(新增const 、$id );Draft 07(引入if/then/else ;OpenAPI 3.0采纳) |
标准化推进 | 2019年至今 | 成立正式组织,规范迭代更结构化,扩展模块化和动态引用能力 | JSON Schema Org成立;Draft 2020-12(重构核心模型,支持动态引用;被OpenAPI 3.1采纳) |
如果不追求较为复杂的功能,Draft-07可以满足大多数定义数据和约束数据的使用场景。
1. 基础类型验证
1.1 type 关键字
定义数据的基本类型。
bash
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string"
}
支持的类型:
string
- 字符串number
- 数字(整数或浮点数)integer
- 整数boolean
- 布尔值array
- 数组object
- 对象null
- 空值
多类型示例:
json
{
"type": ["string", "number"],
"description": "可以是字符串或数字"
}
1.2 enum 枚举
限制值必须是预定义集合中的一个。
json
{
"type": "string",
"enum": ["draft", "published", "archived"],
"description": "文章状态"
}
实际用例:用户角色验证
json
{
"type": "object",
"properties": {
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"],
"description": "用户角色"
},
"permissions": {
"type": "array",
"items": {
"enum": ["read", "write", "delete", "share"]
}
}
}
}
1.3 const 常量(Draft-06+)
值必须严格等于指定常量。
json
{
"const": "v1.0",
"description": "API 版本必须是 v1.0"
}
2. 字符串验证
2.1 长度限制
json
{
"type": "string",
"minLength": 3,
"maxLength": 50,
"description": "用户名:3-50 个字符"
}
2.2 pattern 正则表达式
json
{
"type": "string",
"pattern": "^[a-zA-Z0-9_]+$",
"description": "只允许字母、数字和下划线"
}
实际用例:表单验证
json
{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"pattern": "^[^@]+@[^@]+\.[^@]+$"
},
"phone": {
"type": "string",
"pattern": "^\+?[1-9]\d{1,14}$",
"description": "国际电话格式"
},
"zipCode": {
"type": "string",
"pattern": "^\d{5}(-\d{4})?$",
"description": "美国邮政编码"
}
}
}
2.3 format 格式
Draft-07 支持的常见格式:
json
{
"type": "object",
"properties": {
"email": {"type": "string", "format": "email"},
"website": {"type": "string", "format": "uri"},
"createdAt": {"type": "string", "format": "date-time"},
"birthDate": {"type": "string", "format": "date"},
"ipAddress": {"type": "string", "format": "ipv4"},
"ipv6": {"type": "string", "format": "ipv6"}
}
}
3. 数字验证
3.1 范围限制
json
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": 0,
"exclusiveMaximum": 100
}
注意: Draft-07 中 exclusiveMinimum
和 exclusiveMaximum
是布尔值,与 minimum/maximum 配合使用。
3.2 multipleOf 倍数
json
{
"type": "number",
"multipleOf": 0.5,
"description": "必须是 0.5 的倍数"
}
实际用例:价格和数量
json
{
"type": "object",
"properties": {
"price": {
"type": "number",
"minimum": 0,
"multipleOf": 0.01,
"description": "价格,精确到分"
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 999,
"description": "购买数量"
},
"discount": {
"type": "number",
"minimum": 0,
"maximum": 1,
"exclusiveMaximum": true,
"description": "折扣率 0-1 之间"
}
}
}
4. 对象验证
4.1 properties 和 required
json
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0},
"email": {"type": "string", "format": "email"}
},
"required": ["name", "email"]
}
4.2 additionalProperties
控制是否允许未定义的属性。
json
{
"type": "object",
"properties": {
"id": {"type": "integer"}
},
"additionalProperties": false
}
允许额外属性但限制类型:
json
{
"type": "object",
"properties": {
"name": {"type": "string"}
},
"additionalProperties": {"type": "string"}
}
4.3 propertyNames
验证属性名称。
json
{
"type": "object",
"propertyNames": {
"pattern": "^[a-z]+$"
},
"description": "所有属性名必须是小写字母"
}
4.4 minProperties 和 maxProperties
json
{
"type": "object",
"minProperties": 1,
"maxProperties": 5,
"description": "对象必须有 1-5 个属性"
}
实际用例:API 配置对象
json
{
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"timeout": {
"type": "integer",
"minimum": 0,
"default": 30000
},
"retries": {
"type": "integer",
"minimum": 0,
"maximum": 10,
"default": 3
}
},
"required": ["host"],
"additionalProperties": false
}
4.5 dependencies
属性依赖关系。
简单依赖:
json
{
"type": "object",
"properties": {
"creditCard": {"type": "string"},
"billingAddress": {"type": "string"}
},
"dependencies": {
"creditCard": ["billingAddress"]
},
"description": "如果有信用卡,必须提供账单地址"
}
模式依赖:
json
{
"type": "object",
"properties": {
"type": {"enum": ["personal", "business"]}
},
"dependencies": {
"type": {
"oneOf": [
{
"properties": {
"type": {"const": "personal"},
"firstName": {"type": "string"},
"lastName": {"type": "string"}
},
"required": ["firstName", "lastName"]
},
{
"properties": {
"type": {"const": "business"},
"companyName": {"type": "string"},
"taxId": {"type": "string"}
},
"required": ["companyName", "taxId"]
}
]
}
}
}
5. 数组验证
5.1 items
定义数组元素的模式。
单一模式(所有元素相同):
json
{
"type": "array",
"items": {
"type": "string"
}
}
元组验证(每个位置不同模式):
json
{
"type": "array",
"items": [
{"type": "string"},
{"type": "number"},
{"type": "boolean"}
],
"additionalItems": false,
"description": "[姓名, 年龄, 是否激活]"
}
5.2 contains(Draft-06+)
数组必须包含至少一个符合模式的元素。
json
{
"type": "array",
"contains": {
"type": "number",
"minimum": 5
},
"description": "数组必须包含至少一个大于等于 5 的数字"
}
5.3 长度和唯一性
json
{
"type": "array",
"minItems": 1,
"maxItems": 10,
"uniqueItems": true,
"description": "1-10 个唯一元素"
}
实际用例:标签系统
json
{
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9-]+$",
"minLength": 2,
"maxLength": 30
},
"minItems": 1,
"maxItems": 10,
"uniqueItems": true,
"description": "文章标签"
}
}
}
6. 条件验证 (if/then/else) - Draft-07 新特性
这是 Draft-07 最强大的新特性之一。
6.1 基础用法
json
{
"type": "object",
"properties": {
"country": {"type": "string"}
},
"if": {
"properties": {
"country": {"const": "USA"}
}
},
"then": {
"properties": {
"zipCode": {
"type": "string",
"pattern": "^\d{5}$"
}
},
"required": ["zipCode"]
},
"else": {
"properties": {
"postalCode": {"type": "string"}
}
}
}
6.2 实际用例:动态表单验证
json
{
"type": "object",
"properties": {
"accountType": {
"enum": ["personal", "business"]
},
"age": {"type": "integer"},
"company": {"type": "string"},
"taxId": {"type": "string"}
},
"required": ["accountType"],
"if": {
"properties": {
"accountType": {"const": "personal"}
}
},
"then": {
"properties": {
"age": {
"type": "integer",
"minimum": 18
}
},
"required": ["age"]
},
"else": {
"required": ["company", "taxId"],
"properties": {
"taxId": {
"type": "string",
"pattern": "^\d{2}-\d{7}$"
}
}
}
}
6.3 多条件嵌套
json
{
"type": "object",
"properties": {
"shippingMethod": {
"enum": ["standard", "express", "international"]
},
"weight": {"type": "number"}
},
"if": {
"properties": {
"shippingMethod": {"const": "international"}
}
},
"then": {
"properties": {
"customsValue": {
"type": "number",
"minimum": 0
},
"destinationCountry": {
"type": "string",
"minLength": 2
}
},
"required": ["customsValue", "destinationCountry"]
},
"else": {
"if": {
"properties": {
"shippingMethod": {"const": "express"}
}
},
"then": {
"properties": {
"weight": {
"type": "number",
"maximum": 30
}
}
}
}
}
7. 组合模式
7.1 allOf(必须满足所有)
json
{
"allOf": [
{
"type": "object",
"properties": {
"name": {"type": "string"}
},
"required": ["name"]
},
{
"properties": {
"age": {
"type": "integer",
"minimum": 0
}
}
}
]
}
7.2 anyOf(至少满足一个)
json
{
"anyOf": [
{"type": "string", "minLength": 5},
{"type": "number", "minimum": 0}
],
"description": "必须是长度>=5的字符串或非负数"
}
7.3 oneOf(恰好满足一个)
css
{
"oneOf": [
{
"properties": {
"type": {"const": "email"},
"address": {
"type": "string",
"format": "email"
}
},
"required": ["type", "address"]
},
{
"properties": {
"type": {"const": "phone"},
"number": {
"type": "string",
"pattern": "^\+?[0-9]{10,15}$"
}
},
"required": ["type", "number"]
}
]
}
7.4 not(不能满足)
json
{
"not": {
"properties": {
"role": {"const": "admin"}
}
},
"description": "角色不能是 admin"
}
实际用例:多态数据验证
bash
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "支付方式",
"type": "object",
"required": ["paymentType"],
"properties": {
"paymentType": {
"type": "string",
"enum": ["credit_card", "paypal", "bank_transfer"]
}
},
"oneOf": [
{
"properties": {
"paymentType": {"const": "credit_card"},
"cardNumber": {
"type": "string",
"pattern": "^[0-9]{16}$"
},
"cvv": {
"type": "string",
"pattern": "^[0-9]{3,4}$"
},
"expiryDate": {
"type": "string",
"pattern": "^(0[1-9]|1[0-2])/[0-9]{2}$"
}
},
"required": ["cardNumber", "cvv", "expiryDate"]
},
{
"properties": {
"paymentType": {"const": "paypal"},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["email"]
},
{
"properties": {
"paymentType": {"const": "bank_transfer"},
"accountNumber": {"type": "string"},
"routingNumber": {"type": "string"}
},
"required": ["accountNumber", "routingNumber"]
}
]
}
8. $ref 引用
8.1 内部引用
bash
{
"definitions": {
"address": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
"zipCode": {"type": "string"}
},
"required": ["street", "city"]
}
},
"type": "object",
"properties": {
"billingAddress": {"$ref": "#/definitions/address"},
"shippingAddress": {"$ref": "#/definitions/address"}
}
}
8.2 外部引用
bash
{
"type": "object",
"properties": {
"user": {
"$ref": "user-schema.json"
}
}
}
8.3 递归引用
bash
{
"definitions": {
"node": {
"type": "object",
"properties": {
"value": {"type": "string"},
"children": {
"type": "array",
"items": {"$ref": "#/definitions/node"}
}
}
}
},
"$ref": "#/definitions/node"
}
9. 元数据关键字
9.1 描述性关键字
json
{
"title": "用户配置",
"description": "用户账户的配置选项",
"examples": [
{
"username": "john_doe",
"email": "john@example.com"
}
],
"default": {
"theme": "light",
"notifications": true
}
}
9.2 $comment(Draft-07 新增)
用于内部注释,不影响验证。
bash
{
"type": "object",
"$comment": "这个模式用于验证用户输入,版本 2.0",
"properties": {
"age": {
"type": "integer",
"$comment": "TODO: 考虑添加最大年龄限制"
}
}
}
9.3 readOnly 和 writeOnly(Draft-07 新增)
json
{
"type": "object",
"properties": {
"id": {
"type": "integer",
"readOnly": true,
"description": "只读字段,由系统生成"
},
"password": {
"type": "string",
"writeOnly": true,
"description": "只写字段,不在响应中返回"
},
"username": {
"type": "string"
}
}
}
10. 扩展
1. 扩展的基本原则
JSON Schema 允许在模式中添加任何自定义属性,只要遵循以下规则:
✅ 允许的做法
json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
// 标准关键字
"minLength": 3,
// ⭐ 自定义扩展(不以 $ 开头)
"x-ui-widget": "textarea",
"x-database-column": "user_email",
"customFieldId": "field_123"
}
❌ 禁止的做法
json
{
// ❌ 不要使用 $ 开头(保留给规范)
"$myCustomField": "value",
// ❌ 不要与标准关键字冲突
"type": "custom-type" // type 只能是标准类型
}
2. 命名约定
推荐的命名规范
方式 1:使用 x-
前缀(最常见)
json
{
"type": "string",
"x-display-name": "用户邮箱",
"x-validation-level": "strict",
"x-internal-id": "USR_EMAIL_001"
}
方式 2:使用厂商前缀
json
{
"type": "object",
"aabbcc-encryption": "AES-256",
"aabbcc-retention-days": 90,
"microsoft-sql-type": "NVARCHAR(255)"
}
方式 3:使用域名前缀(最安全)
json
{
"type": "string",
"com.mycompany.ui.widget": "date-picker",
"com.mycompany.db.indexed": true
}
11. 验证

Schema 标准验证
- Ajv JavaScript / TypeScript
- jsonschema Python
扩展验证实现
如果项目中扩展了Schema,那么就要为此制定扩展验证。利用标准验证器会忽略Schema以外的定义的这一特性,承接自定义特性的判断逻辑。
JavaScript
// 自定义验证器示例
class ExtendedValidator {
constructor(schema) {
this.schema = schema;
this.standardValidator = new StandardJSONSchemaValidator();
}
async validate(data) {
// 1. 先执行标准验证
const standardErrors = this.standardValidator.validate(data, this.schema);
if (standardErrors.length > 0) {
return standardErrors;
}
// 2. 执行自定义扩展验证
const customErrors = [];
// 处理异步验证
if (this.schema.properties.username?.['x-validation-async']) {
const isUnique = await this.checkUsernameUniqueness(data.username);
if (!isUnique) {
customErrors.push({
field: 'username',
message: this.schema.properties.username['x-error-messages']?.async
});
}
}
// 处理密码强度
if (this.schema.properties.password?.['x-password-rules']) {
const passwordErrors = this.validatePasswordRules(
data.password,
this.schema.properties.password['x-password-rules']
);
customErrors.push(...passwordErrors);
}
// 处理业务规则
if (this.schema['x-business']?.['workflow']) {
const workflowErrors = await this.validateBusinessWorkflow(data);
customErrors.push(...workflowErrors);
}
return customErrors;
}
async checkUsernameUniqueness(username) {
// 调用 API 检查唯一性
const response = await fetch(`/api/check-username?username=${username}`);
return response.json().then(data => data.available);
}
validatePasswordRules(password, rules) {
const errors = [];
if (rules['require-uppercase'] && !/[A-Z]/.test(password)) {
errors.push({field: 'password', message: 'Must contain uppercase letter'});
}
if (rules['require-lowercase'] && !/[a-z]/.test(password)) {
errors.push({field: 'password', message: 'Must contain lowercase letter'});
}
// ... 更多规则检查
return errors;
}
}
总结
- 不要重复造轮子,做之前最好先快速调研现有的技术,往往会得到现成且成熟的答案(虽然不是100%匹配);
- Schema Draft-07已经足够好,稍加补充就能完美匹配需求;
- 团队内达成共识比技术选型更重要,大家步调一致才能合作创新;
参考资源: