schema校验
# 基础知识
# 定义一个 JSON Schema
$schema
关键字来声明将架构写入哪个版本的 JSON 架构规范。
{ "$schema": "https://json-schema.org/draft/2020-12/schema" }
# 定义唯一标识符
{ "$id": "http://yourdomain.com/schemas/myschema.json" }
# 类型关键词
# string
字符串类型
{ "type": "string" }
检测
# 通过检测
"aaa"
"b"
# number
数字类型
{ "type": "number" }
检测
# 通过检测
11
11.1
# 未通过检测
"11"
# 多个类型
{ "type": ["number", "string"] }
检测
# 通过检测
42
"Life, the universe, and everything"
# 未通过检测
["Life", "the universe", "and everything"]
[1,"Life"]
# 字符串类型
# length
字符串长度
{
"type": "string",
"minLength": 2,
"maxLength": 3
}
检测
# 通过检测
"AB"
"ABC"
# 未通过检测
"A"
"ABCD"
# 正则表达式方式
{
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
检测
# 通过检测
"555-1212"
"(888)555-1212"
# 未通过检测
"(888)555-1212 ext. 532"
"(800)FLOWERS"
# 格式化
JSON 没有日期类型,所以日期需要编码为字符串。
# 日期和时间
日期和时间在中表示。这是日期格式的子集,通常也称为 ISO8601 格式。
"date-time"
:日期和时间在一起,例如,2018-11-13T20:20:39+00:00
。"time"
: new in draft 7时间,例如,20:20:39+00:00
"date"
:草案 7 中的新日期,例如,2018-11-13
。"duration"
:2019-09 草案中的新内容持续时间定义的持续时间。例如,P3D
表示持续时间为 3 天。
# 电子邮件地址
"email"
:互联网电子邮件地址,请参阅RFC 5321,第 4.1.2 节 (opens new window)。"idn-email"
:草案 7 中的新内容 Internet 电子邮件地址的国际化形式,请参阅 RFC 6531 (opens new window)。
# 主机名
"hostname"
:互联网主机名,请参阅RFC 1123,第 2.1 节 (opens new window)。"idn-hostname"
:草案 7 中的新内容国际化互联网主机名,请参阅RFC5890,第 2.3.2.3 节 (opens new window)。
# IP 地址
"ipv4"
: IPv4 地址,根据RFC 2673 第 3.2 节 (opens new window)中定义的点分四组 ABNF 语法。"ipv6"
:IPv6 地址,如RFC 2373,第 2.2 节 (opens new window)中所定义。
# 资源标识符
"uuid"
:2019-09 草案中的新内容RFC 4122 (opens new window)定义的通用唯一标识符。例子:3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a
"uri"
:根据 RFC3986 (opens new window)的通用资源标识符 (URI) 。"uri-reference"
:根据RFC3986 第 4.1 节 (opens new window),草案 6 中的新内容 A URI 参考(URI 或相对参考) 。"iri"
:草案 7 中的新内容 根据RFC3987 (opens new window) ,国际化的“uri”等价物。"iri-reference"
:草案 7 中的新内容 根据RFC3987 ,国际化相当于“uri-reference” (opens new window)
# URI 模板
"uri-template"
:根据 RFC6570 (opens new window)草案 6 A URI 模板(任何级别)中的新内容。如果您还不知道 URI 模板是什么,您可能不需要这个值。
# JSON 指针
"json-pointer"
:根据RFC6901 (opens new window),草案 6 中的新内容 A JSON Pointer 。在 Structuring a complex schema (opens new window)中有更多关于在 JSON Schema 中使用 JSON Pointer 的讨论。请注意,仅当整个字符串仅包含 JSON 指针内容时才应使用此选项,例如 . JSON 指针 URI 片段,例如应该使用 ./foo/bar``#/foo/bar/``"uri-reference"
"relative-json-pointer"
:草案 7 中的新内容 相对 JSON 指针 (opens new window)。
# 正则表达式
"regex"
:草案 7 中的新内容 一个正则表达式,根据ECMA 262 (opens new window) 方言应该是有效的。
# 数字类型
# integer
整数类型
{ "type": "integer" }
检测
# 通过检测
42
-1
1.0 # 小数部分为零的数字被视为整数
# 未通过检测
3.1415926
"42"
# number
数字类型
检测
# 通过检测
42
-1
1.0
3.1415926
# 未通过检测
"42"
# Multiples
倍数
将数字限制为给定数字的倍数
{
"type": "number",
"multipleOf": 10
}
检测
# 通过检测
0
10
20
100
# 未通过检测
0.1
23
# Range
范围
使用 minimum
和maximum
关键字的组合来指定的(或者exclusiveMinimum
用于 exclusiveMaximum
表示排他范围)。
{
"type": "number",
"minimum": 0,
"exclusiveMaximum": 100
}
小于minimum
:
-1
minimum
包含在内,因此 0 有效:
0
10
99
exclusiveMaximum
是独占的,所以 100 无效:
100
大于maximum
:
101
# 对象类型
{ "type": "object" }
// 通过检测
{
"key": "value",
"another_key": "another_value"
}
{
"Sun": 1.9891e30,
"Jupiter": 1.8986e27,
"Saturn": 5.6846e26,
"Neptune": 10.243e25,
"Uranus": 8.6810e25,
"Earth": 5.9736e24,
"Venus": 4.8685e24,
"Mars": 6.4185e23,
"Mercury": 3.3022e23,
"Moon": 7.349e22,
"Pluto": 1.25e22
}
// 未通过检测
// 使用非字符串key是无效键
{
0.01: "cm",
1: "m",
1000: "km"
}
"Not an object"
["An", "array", "not", "an", "object"]
# Properties
属性
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
}
}
有效的
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
number 是错误的类型是无效的
{ "number": "1600", "street_name": "Pennsylvania", "street_type": "Avenue" }
默认情况下,可以省略属性street_type
被省略
{ "number": 1600, "street_name": "Pennsylvania" }
默认情况下,所有的属性都省略也是有效的
{}
默认情况下,提供额外的属性是有效的:direction
额外属性
{
"number": 1600,
"street_name": "Pennsylvania",
"street_type": "Avenue",
"direction": "NW"
}
# Pattern Properties
匹配属性
{
"type": "object",
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
}
}
测试
# 测试通过
{ "S_25": "This is a string" }
{ "I_0": 42 }
{ "keyword": "value" } # 这是一个不匹配任何正则表达式的键:
# 测试未通过
{ "S_0": 42 } # 开头S_,则它必须是一个字符串
{ "I_42": "This is a string" } # 如果名称以 开头I_,则必须是整数
# Additional Properties
附加属性
默认情况下允许任何其他属性,设置additionalProperties
对额外属性进行约束
设置未false
时不允许添加附加属性
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": false
}
测试
# 通过测试
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
# 测试未通过
# direction 为额外属性不能通过测试
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
可以使用非布尔模式对实例的附加属性施加更复杂的约束
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": { "type": "string" }
}
测试
# 有效的:附加属性的值是一个字符串
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
# 无效的:附加属性的值不是一个字符串
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "office_number": 201 }
# Extending Closed Schemas
扩展封闭模式
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"],
"additionalProperties": false
}
],
"properties": {
"type": { "enum": ["residential", "business"] }
},
"required": ["type"]
}
失败additionalProperties
。“type”被认为是额外的。
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business"
}
失败required
。“type”是必需的。
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC"
}
因为additionalProperties
在 allOf 里面认为,是不可以添加额外属性,require
又是必须的导致如何都出错。
解决方法:移至additionalProperties
扩展架构并重新声明扩展架构中的属性
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
],
"properties": {
"street_address": true,
"city": true,
"state": true,
"type": { "enum": ["residential", "business"] }
},
"required": ["type"],
"additionalProperties": false
}
# 测试通过
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business"
}
# 测试未通过
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business",
"something that doesn't belong": "hi!"
}
# Unevaluated Properties
未评估的属性
unevaluatedProperties
除了它可以识别在子模式中声明的属性。
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
],
"properties": {
"type": { "enum": ["residential", "business"] }
},
"required": ["type"],
"unevaluatedProperties": false
}
测试
# 测试通过
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business"
}
# 测试未通过
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business",
"something that doesn't belong": "hi!"
}
unevaluatedProperties
允许你做更复杂的事情,比如有条件地添加属性。
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"type": { "enum": ["residential", "business"] }
},
"required": ["street_address", "city", "state", "type"],
"if": {
"type": "object",
"properties": {
"type": { "const": "business" }
},
"required": ["type"]
},
"then": {
"properties": {
"department": { "type": "string" }
}
},
"unevaluatedProperties": false
}
如果type
的类型为常量business
,则设置department
为string
类型。
# 有效
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business",
"department": "HR"
}
因为type
的类型不是business
,所以department
应该为["street_address", "city", "state", "type"]一种。
# 无效
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "residential",
"department": "HR"
}
# Required Properties
必要属性
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": { "type": "string" },
"telephone": { "type": "string" }
},
"required": ["name", "email"]
}
测试
# 有效
{
"name": "William Shakespeare",
"email": "bill@stratford-upon-avon.co.uk"
}
{
"name": "William Shakespeare",
"email": "bill@stratford-upon-avon.co.uk",
"address": "Henley Street, Stratford-upon-Avon, Warwickshire, England",
"authorship": "in question"
}
# 无效:缺少必要属性
{
"name": "William Shakespeare",
"address": "Henley Street, Stratford-upon-Avon, Warwickshire, England",
}
{
"name": "William Shakespeare",
"address": "Henley Street, Stratford-upon-Avon, Warwickshire, England",
"email": null
}
# Property names
属性名称
检测 key 的有效性
{
"type": "object",
"propertyNames": {
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
}
}
测试
# 有效
{
"_a_proper_token_001": "value"
}
# 无效
{
"001 invalid": "value"
}
# Size
属性的多少
下图为,最少 2 个属性。最多 3 个属性。
{
"type": "object",
"minProperties": 2,
"maxProperties": 3
}
测试
# 有效
{ "a": 0, "b": 1 }
{ "a": 0, "b": 1, "c": 2 }
# 无效
{}
{ "a": 0 }
{ "a": 0, "b": 1, "c": 2, "d": 3 }
# 数组类型
{ "type": "array" }
测试
# 有效
[1, 2, 3, 4, 5]
[3, "different", { "types" : "of values" }]
# 无效
{"Not": "an array"}
# Item
列表验证
{
"type": "array",
"items": {
"type": "number"
}
}
测试
# 有效
[1, 2, 3, 4, 5]
[]
# 无效
[1, 2, "3", 4, 5]
# Tuple validation
元组验证
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
]
}
测试
# 有效
[1600, "Pennsylvania", "Avenue", "NW"]
[10, "Downing", "Street"]
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
# 无效
[24, "Sussex", "Drive"] # Drive 不属于["Street", "Avenue", "Boulevard"]
["Palais de l'Élysée"] # 缺少第一个number
# Additional Items
附加选项
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
],
"items": false
}
测试
# 有效
[1600, "Pennsylvania", "Avenue", "NW"]
[1600, "Pennsylvania", "Avenue"]
# 无效 Washington是无效的
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
允许附加值{ "type": "string" }
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
],
"items": { "type": "string" }
}
测试
# 有效
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
# 无效 20500不是字符串
[1600, "Pennsylvania", "Avenue", "NW", 20500]
# Contains
包含
items
要求每一个必须是同一个类型,contains
至少要包含一个或多个
{
"type": "array",
"contains": {
"type": "number"
}
}
测试
# 有效
["life", "universe", "everything", 42]
[1, 2, 3, 4, 5]
# 无效
["life", "universe", "everything", "forty-two"] # 缺少数字类型
# minContains / maxContains
最少最多包含
{
"type": "array",
"contains": {
"type": "number"
},
"minContains": 2,
"maxContains": 3
}
测试
# 有效
["apple", "orange", 2, 4]
["apple", "orange", 2, 4, 8]
# 无效
["apple", "orange", 2] # 数字类型最少2个
["apple", "orange", 2, 4, 8, 16] # 数字类型最多3个
# Length
数组长度
{
"type": "array",
"minItems": 2,
"maxItems": 3
}
测试
# 有效
[1, 2]
[1, 2, 3]
# 无效
[] # 少于2
[1] # 少于2
[1, 2, 3, 4] # 大于3
# Uniqueness
唯一性
{
"type": "array",
"uniqueItems": true
}
测试
# 有效
[1, 2, 3, 4, 5]
[]
# 无效
[1, 2, 3, 3, 4] # 3出现了重复
# 通用关键字
# 枚举值
{
"enum": ["red", "amber", "green"]
}
测试
# 有效
"red"
# 无效
"blue"
# 常数值
{
"properties": {
"country": {
"const": "United States of America"
}
}
}
测试
# 有效
{ "country": "United States of America" }
# 无效
{ "country": "Canada" }
# 组合模式
# allOf
给定的数据必须对所有给定的子模式有效。
{
"allOf": [{ "type": "string" }, { "maxLength": 5 }]
}
测试
# 有效
"short"
# 无效
"too long" # 超过最长的长度
# anyOf
必须满足任何一个或多个条件
{
"anyOf": [
{ "type": "string", "maxLength": 5 },
{ "type": "number", "minimum": 0 }
]
}
测试
# 有效
"short"
12
# 无效
"too long" # 超过最长的长度
-5 # 最小值0
# oneOf
只能同时满足其中一个条件
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
测试
# 有效
10
9
# 无效
2 # 不满足5的倍数和3的倍数
15 # 同时满足了5的倍数和3的倍数不行
# not
不能是这个类型
{ "not": { "type": "string" } }
测试
# 有效
42
{ "key": "value" }
# 无效
"I am a string" # 不能是字符串类型
# 带条件的子模式
# dependentRequired
依赖
使用关键字表示一个属性对另一个属性的依赖性
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" },
"billing_address": { "type": "string" }
},
"required": ["name"],
"dependentRequired": {
"credit_card": ["billing_address"]
}
}
测试
# 有效
{
"name": "John Doe",
"credit_card": 5555555555555555,
"billing_address": "555 Debtor's Lane"
}
{
"name": "John Doe"
}
{
"name": "John Doe",
"billing_address": "555 Debtor's Lane"
}
# 无效
# credit_card 依赖billing_address属性,但是没有billing_address属性
{
"name": "John Doe",
"credit_card": 5555555555555555
}
# dependentSchemas
依赖 schema
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" }
},
"required": ["name"],
"dependentSchemas": {
"credit_card": {
"properties": {
"billing_address": { "type": "string" }
},
"required": ["billing_address"]
}
}
}
测试
# 有效
{
"name": "John Doe",
"credit_card": 5555555555555555,
"billing_address": "555 Debtor's Lane"
}
{
"name": "John Doe",
"billing_address": "555 Debtor's Lane"
}
# 无效
# 没有billing_address
{
"name": "John Doe",
"credit_card": 5555555555555555
}
# If-Then-Else
条件判断
{
"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]" }
}
}
}
测试
# 有效
{
"street_address": "1600 Pennsylvania Avenue NW",
"country": "United States of America",
"postal_code": "20500"
}
{
"street_address": "1600 Pennsylvania Avenue NW",
"postal_code": "20500"
}
{
"street_address": "24 Sussex Drive",
"country": "Canada",
"postal_code": "K1M 1M4"
}
# 无效
# postal_code 应该为[A-Z][0-9][A-Z] [0-9][A-Z][0-9]
{
"street_address": "24 Sussex Drive",
"country": "Canada",
"postal_code": "10000"
}
# 默认是 const值
{
"street_address": "1600 Pennsylvania Avenue NW",
"postal_code": "K1M 1M4"
}