什么是 JSON Schema?它的作用是什么?
什么是 JSON Schema?
JSON Schema 是一份用 JSON 格式写成的"数据合同",它声明了某类 JSON 数据必须满足的结构、类型和约束规则。你可以把它理解成 JSON 数据的"类型定义 + 校验规则"——不仅规定有哪些字段、字段是什么类型,还能限定取值范围、格式、必填项等。
面试中回答这个问题,记住三个关键词:校验、契约、文档。JSON Schema 同时满足这三个需求,这是它和其他方案的核心差异。
核心作用
数据校验是最根本的用途。拿到一份 JSON,丢给校验器(如 Ajv、python-jsonschema),立刻知道它合不合规,不用手写一堆 if-else。校验器会返回具体的错误路径和原因,排查问题比手动校验快得多。
接口契约——在前后端协作或微服务通信中,JSON Schema 就是双方的数据约定。OpenAPI 3.0 的 schema 字段本质上就是 JSON Schema,用它描述请求体和响应体,API 文档和校验一步到位。不少团队把 Schema 放进代码仓库单独维护,PR 变更时自动触发兼容性检查。
自动生成代码和表单——不少工具能从 Schema 直接生成 TypeScript 类型定义、Go struct、Java POJO,甚至前端表单组件。减少了"文档写了没人看、代码和文档对不上"的问题。反过来看,也有工具(如 typeof-schema)能从现有 TypeScript 类型反向生成 JSON Schema。
一个完整的例子
json{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "User", "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "age": { "type": "integer", "minimum": 0, "maximum": 150 }, "email": { "type": "string", "format": "email" }, "role": { "type": "string", "enum": ["admin", "editor", "viewer"] } }, "required": ["name", "email"] }
这段 Schema 做了几件事:限定整体是 object;name 是 1-100 字符的字符串;age 是 0-150 的整数;email 必须符合邮箱格式(format 关键字);role 只能取三个枚举值之一;name 和 email 是必填字段。
常用约束关键词
| 类别 | 关键词 | 说明 |
|---|---|---|
| 数值 | minimum, maximum, exclusiveMinimum, exclusiveMaximum | 限定数值范围 |
| 字符串 | minLength, maxLength, pattern, format | 长度和正则、格式校验(format 支持 email、uri、date-time 等) |
| 数组 | minItems, maxItems, uniqueItems, prefixItems | 元素数量、去重、元组验证 |
| 对象 | required, additionalProperties, minProperties | 必填字段和额外属性控制 |
| 逻辑 | allOf, anyOf, oneOf, not | 组合条件,相当于 &&、 |
| 条件 | if/then/else | 条件验证,当 if 匹配时必须满足 then |
| 引用 | $ref, $defs | 复用其他 Schema 定义,避免重复 |
和 TypeScript 类型、Zod 的区别
这是面试高频追问点。三者都能做数据约束,但定位不同:
TypeScript 类型是编译时工具,只在开发阶段生效,运行时完全消失。它不能校验从网络请求拿到的 JSON 数据——一个 API 返回了错误字段,TypeScript 不会报错,代码照跑,只是逻辑可能出错。
Zod既能在编译时推导类型,也能在运行时校验数据。但它绑定 JavaScript/TypeScript 生态,Schema 本身是代码而非数据,其他语言无法直接消费。
JSON Schema是语言无关的数据格式,任何语言都有校验器实现。它最大的优势是"数据即文档"——Schema 可以直接放进 OpenAPI 规范、配置文件,被各种工具链消费,也能存到数据库里做动态校验。
实际项目中,TypeScript 类型管开发体验,Zod 管运行时校验,JSON Schema 管跨团队、跨语言的数据契约。三者常常搭配使用,并不互斥。例如:用 JSON Schema 定义 API 契约,用工具生成 TypeScript 类型给前端用,后端用 Ajv 在运行时校验请求体。
实际项目中的应用场景
API 网关层校验——在网关(如 Kong、AWS API Gateway)配置 JSON Schema,非法请求在进入业务逻辑前就被拦截,返回 400 而不是让错误数据一路穿透到数据库。这比在每个 handler 里写校验逻辑高效得多。
配置文件校验——VS Code 的 settings.json、ESLint 的 .eslintrc、GitHub Actions 的 workflow 文件都有对应的 JSON Schema,IDE 能据此提供自动补全和实时错误提示。你在 VS Code 里写 settings.json 时弹出的属性提示,背后就是 JSON Schema 在工作。
表单驱动开发——前端框架(如 react-jsonschema-form)根据 Schema 自动渲染表单,包括输入框类型、校验规则、必填标记。后端只用关心 Schema 定义,前后端各写各的,表单逻辑不用重复实现。
消息队列数据校验——在 Kafka、RabbitMQ 等消息系统中,用 JSON Schema Registry 管理消息格式,消费者拿到消息先校验再处理,防止上游数据格式变更导致下游崩溃。
版本差异需要注意
JSON Schema 经历了 Draft-04、Draft-06、Draft-07、Draft 2019-09、Draft 2020-12 等版本。主要变化:
- Draft-06 起
exclusiveMinimum从布尔值变成独立数值关键字,minimum不再排他 - Draft 2019-09 引入了
$defs替代definitions,新增prefixItems替代items对数组的元组验证,$recursiveRef实现递归 Schema - Draft 2020-12 是当前最新稳定版,用
$dynamicRef替代了$recursiveRef
使用时注意校验器支持的版本。Ajv 默认支持 Draft-07,需要显式配置 ajv@draft202012 才能用新特性。Python 的 jsonschema 库默认支持最新版。
面试追问方向
Q: JSON Schema 能做条件验证吗?
可以,用 if/then/else。比如"当 role 是 admin 时,permissions 数组必填":
json{ "if": { "properties": { "role": { "const": "admin" } } }, "then": { "required": ["permissions"] } }
Q: 如何校验嵌套很深的数据?
用 $ref 引用子 Schema,避免重复定义。配合 $defs(或旧版的 definitions)集中管理公共片段。深层嵌套不会影响校验性能,Ajv 会将整个 Schema 编译成一个校验函数。
Q: JSON Schema 的性能如何? 复杂 Schema 的校验开销不可忽视,但远快于手写校验代码。Ajv 支持 Schema 编译为函数,一次编译多次使用,单次校验通常在微秒级。对于高频场景(如每秒校验上万次请求),建议预编译并缓存。相比之下,python-jsonschema 比 Ajv 慢一个数量级,高并发场景优先选 Ajv。
Q: JSON Schema 和 Protocol Buffers 的 Schema 有什么区别? Protobuf 侧重序列化和跨语言 RPC,自带代码生成和二进制编码,性能更高但不支持动态校验。JSON Schema 侧重验证和文档,数据仍然是 JSON 文本,灵活性更强但序列化体积和速度不如 Protobuf。两者适用场景不同:内部服务间通信选 Protobuf,面向外部 API 或需要人类可读的场景选 JSON Schema。