理解 JSON Schema
什么是 JSON Schema?
JSON Schema 是一个描述和验证 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、number、object、array、boolean、null。
字符串(string)
string 类型用于文本字 符串。它可能包含 Unicode 字符。
长度:
| 属性 | 描述 | 默认值 |
|---|---|---|
| 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。"iri": 国际化的 "uri",参考 RFC3987。"iri-reference":国际化 "uri-reference",参考 RFC3987。- 如果 Schema 中的值具有相对于特定源路径(例如,来自网页的链接)的能力,则通常更好的做法是使用
"uri-reference"(或"iri-reference")而不是"uri"(或"iri")。"uri"仅当路径必须是绝对路径时才应使用。
- URI 模板
"uri-template":URI 模板(任何级别),参考 RFC6570,如果您尚不知道 URI 模板是什么,则可能不需要此值。
- JSON 指针
- 正则表达式
"regex":正则表达式,参考 ECMA 262。
正则表达式(Regular Expressions)
pattern 的格式参考 ECMA 262。
- 单个 unicode 字符(下面的特殊字符除外)与自己匹配。
.:匹配除换行符以外的任何字符。(请注意,换行符的构成在某种程度上取决于您的平台和语言环境,但实际上这并不重要)。^:仅在字符串开头匹配。$:仅在字符串末尾匹配。(...):将一系列正则表达式分组为一个正则表达式。|:匹配|符号之前或之后的正则表达式。[abc]:匹配方括号内的任何字符。[a-z]:匹配字符范围。[^abc]:匹配未列出的任何字符。[^a-z]:匹配范围之外的任何字符。+:匹配前一个正则表达式的一个或多个重复。*:匹配零个或多个前面的正则表达式重复。?:匹配前一个正则表达式的零个或一个重复。+?,*?,??:的*,+和?预选赛都是贪婪的; 它们匹配尽可能多的文本。有时这种行为是不希望的,并且您希望匹配的字符越少越好。(?!x),(?=x):阴性和阳性预测先行。{x}:精确x匹配前面的正则表达式的出现。{x,y}:至少匹配x并且最多y匹配前面的正则表达式。{x,}:匹配x出现的一个或多个前面的正则表达式。{x}?,{x,y}?,{x,}?:上述表达式的惰性版本。
例如:
{
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
数值类型(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)
object JSON 中的对象类型,键值对映射类型,键都必须是字符串。
{ "key": "value" }
属性(Properties)
使用 properties 关键字定义对象上的属性(键值对),值为一个对象。
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
额外属性(Asdditional Properties)
使用 additionalPropertiesis 定义额外的属性,值为 false 或 schema 的对象。
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
},
"additionalProperties": { "type": "string" }
}
必须属性(Required Properties)
- 使用
required关键字声明所需属性的列表。 required关键字接受零个或多个字符串的数组,这些字符串中的每一个都必须是唯一的。
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": { "type": "string" },
"telephone": { "type": "string" }
},
"required": ["name", "email"]
}
属性名称(Property names)
- 使用
propertyNames对象约定属性名 称的规则。 - 对象键无论如何都必须始终为字符串,定义的规则需在
{ "type": "string" }基础上。
{
"type": "object",
"propertyNames": {
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
}
}
属性个数(Size)
使 用 minProperties 和 maxProperties 关键字限制对象的属性数量(非负整数)。
{
"type": "object",
"minProperties": 2,
"maxProperties": 3
}
依赖(Dependencies)
dependencies 关键字允许基于某些特殊性质的存在目的是变化的模式。
- JSON Schema 中有两种形式的依赖关系:
- 属性依赖(Property dependencies) 声明如果存在给定属性,则必须存在某些其他属性。
- 模式依赖(Schema dependencies) 声明存在给定属性时模式会更改。
属性依赖(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"]
}
}
模式依赖(Schema dependencies)
类似于属性依赖项,但是它们不仅可以指定其他必需的属性,还可以扩展模式使其具有其他约束。
例如,这是编写上面属性依赖的另一种方法:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" }
},
"required": ["name"],
"dependencies": {
"credit_card": {
"properties": {
"billing_address": { "type": "string" }
},
"required": ["billing_address"]
}
}
}
正则属性(Pattern Properties)
如前面提到的 additionalProperties 可以指定额外属性。但这还不够,可以使用 patternProperties 关键字,它从正则表达式映射到架构。如果其他属性与给定的正则表达式匹配,则还必须针对相应的模式进行验证。
patternProperties 可以与 additionalProperties 结合使用。properties、 patternProperties、additionalProperties 结合使用时为并集。
{
"type": "object",
"properties": {
"builtin": { "type": "number" }
},
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
},
"additionalProperties": { "type": "string" }
}
数组(array)
用于有序元素,JSON 数组中的每个元素都可以具有不同的类型。
{ "type": "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"
}
}
元组验证(Tuple validation)
如果数组中每个下标的元素都有不同的含义,则需要定义不同的类型。如:
[编号,街道名称,街道类型,方向]
为此,我们将 items 关键字设置为数组,其中每个索引的元素对应于的不同类型定义。在验证数据的时候可以不提供所 有元素,而且默认情况下也可以在结尾添加其他额外的元素。
{
"type": "array",
"items": [
{
"type": "number"
},
{
"type": "string"
},
{
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
{
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
}
]
}
但当 additionalItems 是 false,不能在数组结尾包含额外的元素。additionalItems 也可以声明为一个对象。
{
"type": "array",
"items": [
{
"type": "number"
},
{
"type": "string"
},
{
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
{
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
}
],
"additionalItems": { "type": "string" }
}
长度(Length)
可以使用 minItemsand、 maxItems关键字指定数组的长度(非负数)。如下长度范围为 2 ~ 3:
{
"type": "array",
"minItems": 2,
"maxItems": 3
}
唯一(Uniqueness)
将 uniqueItems 关键字设置为 true 可以确保数组中的每个元素都是唯一的。空数组也符合。
{
"type": "array",
"uniqueItems": true
}
布尔(boolean)
布尔类型仅匹配两个特殊值:true 和 false。注意:只认可 true 或 false,0 和 1 不被接受;"true" 也不被接受。
{ "type": "boolean" }
空值(null)
空类型通常用于表示缺失值。当架构指定 type 为 null,它只有一个可接受的值:null。
{ "type": "null" }
通用关键字(Generic keywords)
注释(Annotations)
JSON Schema 包括几个关键字 title,description,default,examples 不严格用于验证,但用来描述一个模式的一部分。这些注释属性都不是必须的,但鼓励添加这些描述。
- 在
title和description关键字必须是字符串。“title” 将最好是简短的,而 “description” 应该提供关于改 schema 的目的进行详 细的描述。 default关键字指定的项目的默认值。JSON 处理工具可能会使用此信息为缺少的键/值对提供默认值。非必须。examples关键字是用于提供一系列针对该模式进行验证的示例的地方。
{
"title": "Match anything",
"description": "This is a schema that matches anything.",
"default": "Default value",
"examples": ["Anything", 4035]
}
评论(Comments)
$comment 关键字是严格用于添加注释/批注的 JSON 模式源。它的值必须始终是一个字符串。评论/批注留给 JSON 模式的未来编辑者很有用(很可能是您将来的自己),但不应用于与模式用户进行通信。
枚举值(Enumerated values)
enum 关键字被用于限制值,以一个固定的一组值。它必须是一个至少包含一个元素的数组,其中每个元素都是唯一的。且 type 和 enum 是交集,必须同时满足。
{
"type": "string",
"enum": ["red", "amber", "green"]
}
常量值(Constant values)
const 关键字被用于限制单一的值。
{
"properties": {
"country": {
"const": "United States of America"
}
}
}
const 是 enum 的语法糖。下面两个定义是等价的。
{ "const": "United States of America" }
{ "enum": [ "United States of America" ] }
媒体:字符串编码非 JSON 数据(Media: string-encoding non-JSON data)
contentMediaType
contentMediaType 关键字指定的 MIME 类型的字符串的内容,参考 RFC 2046。
contentEncoding
contentEncoding 关键字指定的编码用于存储内容,参考 RFC 2054,部分 6.1。
可接受的值为7bit,8bit,binary,quoted-printable 和 base64。如果未指定,则编码与包含的 JSON 文档相同。
在不深入探讨每种编码的底层细节的情况下,实际上只有两个对现代用法有用的选项:
- 如果内容的编码方式与随附的 JSON 文档相同(出于实际目的,几乎总是 UTF-8),则请保持
contentEncoding未指定状态,并将内容原样包含在字符串中。这包括基于文本的内容类型,例如text/html或application/xml。 - 如果内容是二进制数据,请设置
contentEncoding为base64并使用 Base64 对内容进行编码。这将包括许多图像类型,例如image/png或音频类型,例如audio/mpeg。
例子
指定该字符串包含一个 HTML 文档,该 HTML 文档使用与周围文档相同的编码进行编码:
{
"type": "string",
"contentMediaType": "text/html"
}
指示字符串包含使用 Base64 编码的 PNG 图像:
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/png"
}
结合模式(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,给定的数据必须对给定的子方案之一有效。有且只能满足一个条件。
如下例中 15 是不能满足的:
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
不(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#"
进阶(Advanced)
如果需要声明您的模式是针对特定版本的 JSON Schema 标准编写的,则应在路径中包括草案名称,例如:
http://json-schema.org/draft-07/schema#http://json-schema.org/draft-06/schema#http://json-schema.org/draft-04/schema#
此外,如果您已经扩展了 JSON 模式语言,使其包含用于验证值的自定义关键字,则可以将自定义 URI 用于 $schema。它不能是上述预定义的值之一,并且可能应包含您拥有的域名。