博客

JSON Schema 入门指南:定义与验证数据结构

JSON 天生灵活——任何键都可以持有任意类型的值,结构本身没有强制约束。这种灵活性在原型阶段很方便,但在生产系统中却暗藏风险,因为你通常期望特定的字段、特定的类型和特定的约束条件。JSON Schema 通过提供一种声明式语言来解决这个问题,让你能够精确描述合法 JSON 的样子。

什么是 JSON Schema?

JSON Schema 是用于标注和验证 JSON 文档的一套词汇规范。它本身也是用 JSON 编写的,易于阅读、编写和工具链集成。当前稳定规范为 Draft 2020-12,由 json-schema.org 维护。

Schema 描述了一个 JSON 值的预期结构。验证器使用 schema 来检查给定文档是否符合要求——如果不符合,则精确报告哪些约束被违反以及位置所在。

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
}

这个 schema 接受任何包含 name 字符串字段的对象。age 字段是可选的,但如果存在,它必须是非负整数。

基本类型定义

JSON Schema 直接映射到 JSON 的六种原生类型。

字符串(string)

{ "type": "string" }

匹配任何 JSON 字符串值。你可以添加最小长度和最大长度约束:

{
"type": "string",
"minLength": 1,
"maxLength": 100
}

数字(number)与整数(integer)

{ "type": "number" }
{ "type": "integer" }

number 同时接受整数和浮点数。integer 只接受整数。使用 minimummaximumexclusiveMinimumexclusiveMaximum 添加范围约束。

{
"type": "number",
"minimum": 0,
"maximum": 1
}

布尔值(boolean)

{ "type": "boolean" }

只匹配 truefalse

空值(null)

{ "type": "null" }

只匹配 JSON 的 null 值。通常通过数组与其他类型组合使用:

{ "type": ["string", "null"] }

对象(object)

{
"type": "object",
"properties": {
"id": { "type": "integer" },
"email": { "type": "string" }
}
}

properties 关键字定义命名字段。默认情况下所有属性都是可选的。

数组(array)

{
"type": "array",
"items": { "type": "string" }
}

items 关键字约束每个元素的类型。添加 minItemsmaxItems 来控制数组长度。

必填字段与可选字段

默认情况下,对象 schema 中的所有属性都是可选的。使用 required 关键字强制要求字段存在:

{
"type": "object",
"properties": {
"username": { "type": "string" },
"email": { "type": "string" },
"bio": { "type": "string" }
},
"required": ["username", "email"]
}

这里 usernameemail 必须存在,bio 是可选的。注意 requiredproperties 的兄弟节点,而不是嵌套在其内部。

如果要完全禁止未知属性,添加 "additionalProperties": false

{
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"],
"additionalProperties": false
}

高级特性

enum

将值限制为一组固定的允许选项:

{
"type": "string",
"enum": ["draft", "published", "archived"]
}

enum 也可以跨类型使用——你可以在同一个列表中枚举任意 JSON 类型的特定值。

pattern

用正则表达式验证字符串:

{
"type": "string",
"pattern": "^[a-z0-9_-]{3,30}$"
}

适用于 slug、用户名、标识符以及任何格式可预期的字符串。

format

为常见字符串格式提供语义验证:

{ "type": "string", "format": "email" }
{ "type": "string", "format": "date" }
{ "type": "string", "format": "uri" }
{ "type": "string", "format": "uuid" }

注意:format 验证在规范中是可选的。部分验证器默认启用它,其他验证器需要显式配置。

minimum 与 maximum

前面已介绍过数字的用法,同样适用于 integer

{
"type": "integer",
"minimum": 1,
"maximum": 100
}

$ref 与 Schema 组合

$ref 允许你引用另一个 schema 定义,从而实现复用并避免重复:

{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string" }
},
"required": ["street", "city", "country"]
}
},
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" }
}
}

用于组合 schema 的关键字包括 allOfanyOfoneOfnot

关键字含义
allOf必须对列出的所有 schema 均有效
anyOf必须对至少一个 schema 有效
oneOf必须对恰好一个 schema 有效
not不能对给定 schema 有效

实际应用场景

API 文档

JSON Schema 是 OpenAPI(前身为 Swagger)的基础。OpenAPI 规范中每个请求体和响应体都用 JSON Schema 来描述。编写 schema 迫使你将隐式假设显式化,从而提升文档质量和客户端代码的可靠性。

表单生成

react-jsonschema-formjsonforms 等库读取 JSON Schema 并自动生成带有内置验证的表单 UI。一份 schema 定义同时驱动后端验证和前端渲染。

代码中的数据验证

大多数语言都有成熟的 JSON Schema 验证库:

// JavaScript — ajv
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
# Python — jsonschema
from jsonschema import validate, ValidationError
try:
validate(instance=data, schema=schema)
except ValidationError as e:
print(e.message)

CI 流水线验证

提交钩子或 CI 步骤可以自动对照 schema 验证 JSON 配置文件,在结构性错误进入生产环境之前就将其拦截。

使用 JSONKit 的 Schema 工具

JSONKit 的 Schema 工具提供了一个交互式环境,用于处理 JSON Schema:

  • 并排粘贴 JSON Schema 和 JSON 文档
  • 即时运行验证,查看每个字段的错误信息
  • 从现有 JSON 文档生成初始 schema
  • 探索使用 allOfanyOf$ref 进行 schema 组合

从 JSON 生成 schema 的功能特别实用——当你已有现成的数据集,希望快速生成描述其结构的 schema 时,这个功能能大幅节省时间。

总结

JSON Schema 为 JSON 数据带来了类型安全和结构契约,无需编译步骤,也不依赖特定编程语言。它工作在协议层面——服务之间、团队之间、前端与后端之间——使其成为开发者数据验证工具箱中适用范围最广的工具之一。从简单的类型定义和必填字段开始,随着数据模型的成熟,逐步加入 patternenum$ref 等约束条件。