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"]}]}}}
