JSON Schema 是一个描述和验证 JSON 数据结构的强大工具。Schema 可以理解为模式或规则。
文档:https://json-schema.org/understanding-json-schema/
定义一个最简单的 JSON Schema:{} 它包含许多关键字/属性。如:
关键字 | 描述 | 事例 |
---|---|---|
$schema | 声明 JSON Schema 状态(版本/规范控制) | { “$schema”: “http://json-schema.org/schema#“ } |
$id | 唯一标识 URI | { “$id”: “http://yourdomain.com/schemas/myschema.json“ } |
title | 标题 | “Production” |
description | 描述 | “a produciton info description” |
type | 类型 | “number” | [“number”,”string”] |
properties | 属性配置 | { “test”: { “type”: “number” } } |
下面解析 JSON Schema 中包含的关键字含义及用法。
类型关键字
字符串(string)
长度:
属性 | 描述 |
---|---|
minLength | 字符串最小长度,非负数 |
maxLength | 字符串最大长度,非负数 |
pattern | 正则表达式 |
format | 格式 |
内置 format 列表:
- 日期和时间
- “date-time”:日期和时间,如 2018-11-13T20:20:39+00:00。
- “time”:时间,如 20:20:39+00:00。
- “date”:日期,如 2018-11-13。
- Email 地址
- “email”:互联网电子邮件地址,请参阅 RFC 5322,第 3.4.1 节。
- “idn-email”:电子邮件地址的国际化形式,请参阅 RFC 6531。
- 主机名
- “hostname”:主机名,请参阅 RFC 1034 第 3.1 节。
- “idn-hostname”:国际化的主机名,请参阅 RFC5890 第 2.3.2.3 节。
- IP 地址
- “ipv4”:IPv4 地址,根据 RFC 2673 第 3.2 节中定义的点分四进制 ABNF 语法。
- “ipv6”:IPv6 地址,如 RFC 2373 第 2.2 节中所定义。
- 资源标识符
- “uri”:通用资源标识符(URI),根据 RFC3986。
- “uri-reference”:URI 参考(URI 或相对参考), 参考 RFC3986, section 4.1。
- URI 模板
- “uri-template”:URI 模板(任何级别),参考 RFC6570,如果您尚不知道 URI 模板是什么,则可能不需要此值。
- JSON 指针
- 正则表达式
- “regex”:正则表达式,参考 ECMA 262。
{
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
- “regex”:正则表达式,参考 ECMA 262。
数值类型(Numeric types)
- 整数: integer 用于定义整数类型。
- { “type”: “integer” }。
- 基于 JavaScript 的验证器可以接受 1.0 为整数,而基于其他语言可能不接受,可以巧妙地使用 multipleOf 关键字 { “type”: “number”, “multipleOf”: 1.0 }。
- 数字: number 用于定义任何数字类型,整数或浮点数。
- { “type”: “number” }
- 倍数:multipleOf 可以将数字限制为给定数字(任何正数)的倍数。
- {“type”: “number”, “multipleOf” : 10 }
- 区间(范围):使用 minimum 和 maximum 关键字组合来指定数字范围(或用 exclusiveMinimum 与 exclusiveMaximum 指定单独范围)。
- x ≥ minimum,指定 minimum 最小值且包含该值。
- x > exclusiveMinimum,指定 exclusiveMinimum 最小值不包含该值。
- x ≤ maximum,指定 maximum 最大值且包含该值。
- x < exclusiveMaximum,指定 exclusiveMaximum 最大值不包含该值。
对象(object)
属性(Properties)
使用 properties 关键字定义对象上的属性(键值对),值为一个对象。
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
必须属性(Required Properties)
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": { "type": "string" },
"telephone": { "type": "string" }
},
"required": ["name", "email"]
}
属性个数(Size)
{
"type": "object",
"minProperties": 2,
"maxProperties": 3
}
属性依赖(Property dependencies)
在下面的示例中,每当提供信用卡 credit_card 属性时,账单 billing_address 也必须存在:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" },
"billing_address": { "type": "string" }
},
"required": ["name"],
"dependencies": {
"credit_card": ["billing_address"]
}
}
但依赖性不是双向的,即数据只存在账单 billing_address 不存在信用卡 credit_card 也是正确的。当然可以定义双向依赖性:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" },
"billing_address": { "type": "string" }
},
"required": ["name"],
"dependencies": {
"credit_card": ["billing_address"],
"billing_address": ["credit_card"]
}
}
附加属性(Asdditional Properties)
使用 additionalPropertiesis 定义额外的属性,值为 false 或 schema 的对象。
例如下面的例子,允许要附加属性,但类型必须是 string
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
},
"additionalProperties": { "type": "string" }
}
正则属性(Pattern Properties)
properties、 patternProperties 、additionalPropertiesis 结合使用时为并集
{
"type": "object",
"properties": {
"builtin": { "type": "number" }
},
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
},
"additionalProperties": { "type": "string" }
}
数组(array)
元素(items)
默认情况下,数组的元素可以是任何东西。但也可以根据使用 items, additionalItems 和contains 关键字验证数组的元素。 JSON 数组通常有两种方式:
- 列表验证(List validation): 任意长度的序列,其中每个项目都匹配相同的模式(schema)。
- 元组验证(Tuple validation): 固定长度的序列,其中每个项目可能具有不同的模式(schema)。
列表验证(List validation)
- 每个项目都匹配相同模式,items 关键字设置为单个模式,验证数组中的所有项目,空数组也符合。
- 当 items 为单个模式(为一个对象)时,additionalItems 关键字是没有意义的,因此不应使用。
如下,所有元素都为数字:
{
"type": "array",
"items": {
"type": "number"
}
}
contains 模式仅需要针对数组中的一个或多个项目进行验证。如下,只需包含至少一个数字元素:
{
"type": "array",
"contains": {
"type": "number"
}
}
长度(Length)
{
"type": "array",
"minItems": 2,
"maxItems": 3
}
唯一(Uniqueness)
{
"type": "array",
"uniqueItems": true
}
布尔(boolean)
{ "type": "boolean" }
通用关键字(Generic keywords)
注释(Annotations)
{
"title": "Match anything",
"description": "This is a schema that matches anything.",
"default": "Default value",
"examples": ["Anything", 4035]
}
枚举值(Enumerated values)
enum 关键字被用于限制值,以一个固定的一组值。它必须是一个至少包含一个元素的数组,其中每个元素都是唯一的。且 type 和 enum 是交集,必须同时满足。
{
"type": "string",
"enum": ["red", "amber", "green"]
}
常量值(Constant values)
const 是 enum 的语法糖。下面两个定义是等价的。
{ "const": "United States of America" }
{ "enum": [ "United States of America" ] }
结合模式(Combining schemas)
JSON 模式包括一些用于将模式组合在一起的关键字。
- allOf 必须对所有子方案均有效
- anyOf 必须是有效对抗任何的子模式的
- oneOf 必须确保有效的只有一个的子模式的
- not 必须不能是对给定的模式是有效的,即非在声明规则
allOf,anyOf,oneOf 关键字都必须设置为一个数组,其中每个项目都是独立一个定义,即数组中列出的模式彼此之间一无所知。
所有的(allOf)
要针对进行验证 allOf,给定的数据必须对所有给定的子方案都有效。且子方案有交集。
{
"allOf": [{ "type": "string" }, { "maxLength": 5 }]
}
任何(anyOf)
验证 anyOf,给定的数据必须针对任何(一个或多个)给定的子方案有效。并集。
{
"anyOf": [{ "type": "string" }, { "type": "number" }]
}
一个(oneOf)
验证 oneOf,给定的数据必须对给定的子方案之一有效。有且只能满足一个条件。
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
或
{
"oneOf": [
{ "required": ["name","age"]},
{ "required": ["name","addr"]}
]
}
不(not)
not 关键字声明一个实例进行验证,要求验证的数据它不符合定义的子模式。如下例,非字符串类型:
{ "not": { "type": "string" } }
有条件地应用子模式
if,then 和 else 关键字。规则为如果 if 有效,则 then 也必须有效(并被 else 忽略。)如果 if 无效,则 else 必须有效(并被then忽略)。
例如定义不同地方的邮政编码:如果地址在美国,则 postal_code 字段为五个数字后跟一个可选的四位后缀。如果地址在加拿大,则该 postal_code字段为六位字母数字字符串,字母和数字交替出现。
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"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]" }
}
}
}
$schema 关键字
$schema 关键字被用于声明一个 JSON 片段。它还声明了针对该架构编写的 JSON Schema 标准版本。建议所有 JSON 模式都有一个 $schema 条目,该条目必须位于根目录下。
"$schema": "http://json-schema.org/schema#"
构建复杂的模式
重用
复用部分建议将其放在父架构下的键 definitions 下。
{
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
}
}
$ref 属性
$ref 关键字从其他地方引用此模式片段。值为是 URI 的引用,# 符号(“片段”或“命名锚”)后的部分为JSON Pointer的格式。
{ "$ref": "#/definitions/address" }
如果您使用的是同一文档中的定义,则该 $ref 值以井号(#)开头。之后,以斜杠分隔的项目遍历文档中对象中的键。因此,在我们的示例中 “#/definitions/address” 意味着:
- 转到该文档的根
- 找到 “definitions” 键的值。
- 在该对象中,找到键 “address” 的值
$ref 可以解析为引用另一个文件的 URI,因此,如果您希望将定义包含在单独的文件中,也可以这样做。例如:
{ "$ref": "definitions.json#/address" }
所以完整事例为:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
},
"properties": {
"billing_address": { "$ref": "#/definitions/address" },
"shipping_address": { "$ref": "#/definitions/address" }
}
}
递归
$ref 元素可用于创建引用自己的递归模式。例如,您可能有一个 person 架构,其中包含一个数组 children,每个数组也是 person 实例。
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"person": {
"type": "object",
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
"items": { "$ref": "#/definitions/person" },
"default": []
}
}
}
},
"type": "object",
"properties": {
"person": { "$ref": "#/definitions/person" }
}
}
$id 属性
$id属性是一个 URI 引用,有两个用途:
- 它为模式声明了唯一的标识符。
它声明了一个基本 URI,$ref 可以针对该基本 URI 引用进行解析。
{
"$id": "https://example.com/schemas/customer",
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"shipping_address": { "$ref": "/schemas/address" },
"billing_address": { "$ref": "/schemas/address" }
},
"required": ["first_name", "last_name", "shipping_address", "billing_address"]
}
上面的例子 { “$ref”: “/schemas/address” } == { “$ref”: “https://example.com/schemas/address“ }
扩展
$ref 真正的亮点在于使用 allOf,anyOf 和 oneOf 组合的时候。
比如我们定义了一个基础的类型,我们可以使用allOf 去扩展这个类型
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
},
"type": "object",
"properties": {
"billing_address": { "$ref": "#/definitions/address" },
"shipping_address": {
"allOf": [
{ "$ref": "#/definitions/address" },
{
"properties": { "type": { "enum": ["residential", "business"] } },
"required": ["type"]
}
]
}
}
}