JSON Schema 用于描述 JSON 数据的声明性格式。比如表示一个人的信息 的 JSON:
{
"first_name": "George",
"last_name": "Washington",
"birthday": "1995-02-22",
"address": {
"street_address": "3200 Mount Vernon Memorial Highway",
"city": "Mount Vernon",
"state": "Virginia",
"country": "United States"
}
}
那么,描述这个 JSON 的 JSON Schema 就是:
{
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date-time" },
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
在 JSON 模式中,空对象 或 true
是一个完全有效的模式,它将接受任何有效的 JSON,或者 false 表示不匹配任何内容的模式。
JSON Schema的关键字
type
关键字是 JSON Schema 的基础。它指定 Schema 的数据类型。其中,JSON Schema 的基本类型包括 string 、number、object、array、boolean、null。
type
关键字可以是一个字符串或数组:
- 如果是字符串,则是上述基本类型之一的名称。
- 如果是数组,则必须是字符串数组,其中每个字符串是其中一种基本类型的名称,每个元素都是唯一的。在这种情况下,如果 JSON 片段与_任何_给定类型匹配,则它是有效的。
string 类型
可以使用 minLength
和 maxLength
关键字来限制字符串的长度。对于这两个关键字,该值必须是非负数:
{
"type": "string",
"minLength": 2,
"maxLength": 3
}
pattern
关键字用于将字符串限制为特定的正则表达式:
{
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
format
关键字允许对常用的某些类型的字符串值进行基本语义验证:
/**
format 的可能取值:
"date-time":日期和时间在一起,例如, 2018-11-13T20:20:39+00:00。
"time":draft7的时间,例如,20:20:39+00:00
"date":draft7的日期,例如,2018-11-13。
"hostname": Internet 主机名
"idn-hostname":国际化 Internet 主机名
"ipv4":IPv4 地址
"ipv6":IPv6 地址
"uri":通用资源标识符 (URI) 。
"uri-reference":一个 URI 引用(URI 或相对引用)
"iri":“uri”的国际化等价物。
"iri-reference":“uri-reference”的国际化等价物
"uri-template":一个 URI 模板(任何级别)
"json-pointer":一个 JSON 指针
"relative-json-pointer":一个相对 JSON 指针。
"regex":正则表达式。
*/
{
"type": "string",
"format": "date-time"
}
number 类型
integer
类型用于整数。JSON 没有针对整数和浮点值的不同类型。因此,有无小数点并不足以区分整数和非整数:
{ "type": "integer" }
1.0 // ok
1 // ok
multipleOf
关键字将数字限制为给定数字的倍数 。它可以设置为任何正数:
{
"type": "number",
"multipleOf" : 10
}
minimum
和maximum
关键字的组合指定的 数字的范围(或exclusiveMinimum
和 exclusiveMaximum
用于表示排他范围):
{
"type": "number",
"minimum": 0,
"exclusiveMaximum": 100
}
object 类型
对象是 JSON 中的映射类型。他们将“键”映射到“值”。在 JSON 中,“键”必须始终是字符串。
properties
关键字定义对象的属性(键值对)的 。properties
的值是一个对象,其中每个键是属性的名称,每个值是用于验证该属性的模式。此properties
关键字将忽略与关键字中的任何属性名称不匹配的任何属性。
additionalProperties
关键字用于控制的额外的东西。默认情况下,允许任何其他属性。将additionalProperties
架构设置 为false
意味着不允许其他属性:
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": false
}
也可以给 additionalProperties
使用非布尔模式对实例的其他属性设置更复杂的约束:
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": { "type": "string" }
}
patternProperties
将正则表达式映射到模式。如果属性名称与给定的正则表达式匹配,则属性值必须针对相应的架构进行验证:
{
"type": "object",
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
}
}
required
关键字提供必选的属性列表,采用零个或多个字符串的数组。这些字符串中的每一个都必须是唯一的:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": { "type": "string" },
"telephone": { "type": "string" }
},
"required": ["name", "email"]
}
minProperties
和maxProperties
关键字来限制对象上的属性数量 。这些中的每一个都必须是非负整数:
{
"type": "object",
"minProperties": 2,
"maxProperties": 3
}
array 类型
不加限制的数组类型的模式,元素可以是不同类型:
{ "type": "array" }
[1, 2, 3, 4, 5] // OK
[3, "different", { "types" : "of values" }] // OK
items
关键字设置为单个模式,将用于验证数组中所有元素:
{
"type": "array",
"items": {
"type": "number"
}
}
当items
是单模式时,additionalItems
关键字没有意义,不应使用。
items
关键字设置为一个数组,逐个指定,其中每个项目都是一个模式,对应于JSON 数组的每个索引:
{
"type": "array",
"items": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
]
}
additionalItems
关键字控制如果有超过元组内 items
属性定义的附加元素,元组是否有效。additionalItems如果设置为false,则数组中元素只可以不大于模式个数。同样可以使用非布尔模式来限制附加项可以具有的值来表达更复杂的约束。
minItems
和 maxItems
关键字指定数组的长度。每个关键字的值必须是非负数:
{
"type": "array",
"minItems": 2,
"maxItems": 3
}
contains
关键字指定模式只需要针对数组中的存在进行验证:
{
"type": "array",
"contains": {
"type": "number"
}
}
["life", "universe", "everything", 42] // OK,包含一个number元素
["life", "universe", "everything", "forty-two"] // not OK,不包含number元素
[1, 2, 3, 4, 5] // OK
uniqueItems
关键字设置为true
,可以限制数组中的每个元素都是唯一的:
{
"type": "array",
"uniqueItems": true
}
[1, 2, 3, 4, 5] // OK
[1, 2, 3, 3, 4] // not OK
[] // OK空数组总是通过
boolean 类型和null 类型
boolean 只匹配(===)两个特殊值:true
和 false
。null 只匹配(===)一个值:null。
enum 枚举
enum
关键字用于将值限制为一组固定的值。它必须是一个包含至少一个元素的数组,其中每个元素都是唯一的:
{
"enum": ["red", "amber", "green", null, 42]
}
"red" // OK
null // OK
42 // OK
0 // not OK
常量
const
关键字被用于限制值为一个常量值。
通用关键字
readOnly
和writeOnly
通常用于 API 上下文中。readOnly
表示该值可读不可改,可用于说明一个更改值的PUT请求将得到一个400 Bad Request
的响应。writeOnly
表示该值可已修改但是不可以读,可用于说明可通过PUT请求来设置值,但通过GET请求来检索该记录时不能获取该值 。
default
关键字指定一个默认值。该值不用于在验证过程中填充缺失值。
title
和description
关键字必须是字符串。title最好是简短的,而description提供模式描述的数据因此会有更长的说明。
合并类型
anyOf
关键字模式指定数据至少满足一个给定子模式。
oneOf
关键字模式指定数据必须只满足一个给定子模式。
not关键字模式指定数据不能满足任意给定子模式。
条件类型
if
,then
和else
关键字允许基于另一种模式的结果来应用子模式,当符合if
子schema条件,则对then
的子schema条件进行校验,若if
子schema条件不通过,则走else
子schema条件校验:
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"default": "United States of America",
"enum": ["United States of America", "Canada"]
}
},
"if": {
"properties": { "country": { "const": "United States of America" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
},
"else": {
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
}
结合 allof 关键字,可以 达到 if... else if ... else .... 的效果:
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"default": "United States of America",
"enum": ["United States of America", "Canada", "Netherlands"]
}
},
"allOf": [
{
"if": {
"properties": { "country": { "const": "United States of America" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
}
},
{
"if": {
"properties": { "country": { "const": "Canada" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
},
{
"if": {
"properties": { "country": { "const": "Netherlands" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
}
}
]
}
递归
$ref
关键字引用自己的子schema片段,实现递归模式,可以用于树形结构的描述 :
{
"type": "object",
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
/** #代表最外层 */
"items": { "$ref": "#" }
}
}
}
/**
{
"name": "Elizabeth",
"children": [
{
"name": "Charles",
"children": [
{
"name": "William",
"children": [
{ "name": "George" },
{ "name": "Charlotte" }
]
},
{
"name": "Harry"
}
]
}
]
}
*/
但是请注意,$ref
对另一个的引用$ref
可能会导致解析器中的无限循环,并且是明确禁止的
JSON Schema 的使用场景
1. 数据验证
利用 jsonschema 库对数据做验证:
import { Validator } from 'jsonschema';
const v = new Validator();
const instance = 4;
const schema = {"type": "number"};
console.log(v.validate(instance, schema));
在实际开发中,通过预先写好的脚本对返回接口进行批量的数据校验。
2. 根据 JSON Schema 生成数据采集 UI
由于每一种 schema 类型其实可以对应了一种 UI 展示,那么一条 Schema 其实是可以生成一个表单。在表单的 UI 逻辑中保证在提交表单前,数据是符合 Schema 规则的,表单验证通过后,得到的就是符合 Schema 的 JSON 数据。也就是说,基于符合 Schema 规则的配置,逻辑上就能自动渲染出功能完整的表单UI。
3. 生成JSON格式可读文档
可以基于现有的 JSON 来生成 JSON Schema,快速生成 JSON 格式规范,相当于一份可读文档,减少了团队之间的沟通理解成本。JSON Schema Tool
4. 提供更为准确可靠的mock数据
基于JSON Schema提供多种校验约束条件,可以使用它原生的能力来生成更为准确可靠的mock数据。Fake your JSON-Schemas!