「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」。
1 什么是JSON?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于JavaScript的一个子集,易于人的编写和阅读,也易于机器解析。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java,Go、JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。JSON官网
2 JSON数据结构说明
JSON的核心是建立在以下数据结构之上的,JSON由两种结构组成:
- 键值对的无序集合——对象(或者叫记录、结构、字典、哈希表、有键列表或关联数组等)
- 值的有序列表——数组
2.1 object
{ "key1": "value1", "key2": "value2" }
2.2 array
[ "first", "second", "third" ]
2.3 number
42
3.1415926
2.4 string
"This is a string"
2.5 boolean
true
false
2.6 null
null
2.7 JSON示例
使用这些简单的数据类型,可以表示各种结构化数据。JSON的灵活性使得同一概念可以用无数种方式来表示。例如,可以用JSON以不同的方式表示一个人的信息:
{
"name": "zhangsan",
"mobile": "13688089999",
"hobby":[ "Sing", "Travel", "Football" ],
"age": 30,
"birthday": "February 22, 1990",
"address": "China, chengdu, Chunxi Road"
}
{
"name": "zhangsan",
"mobile": "13688089999",
"hobby":[ "Sing", "Travel", "Football" ],
"age":30,
"birthday": "1990-02-22",
"address": {
"street_address": "Chunxi Road",
"city": "chengdu",
"country": "China"
}
}
2.8 JSON Schema示例优化
尽管上面其中一种表述明显比另一种更正式,但是这两种表述都同样有效。然而,当应用程序说 “给我一个JSON记录”时,准确地知道该记录应该如何组织是很重要的。例如,我们需要知道需要哪些字段,以及值是如何表示的。这就是 JSON Schema 的用武之地。下面的 JSON Schema 片段描述了上面第二个示例的结构。现在不要太担心细节问题。它们将在后面的章节中进行解释。
{
"$schema":"http://json-schema.org/draft-07/schema",
"$id":"http://example.com/example.json",
"type":"object",
"title":"The Root Schema",
"description":"The root schema comprises the entire JSON document.",
"required":[
"name",
"mobile",
"hobby",
"age",
"birthday",
"address"
],
"properties":{
"name":{
"$id":"#/properties/name",
"type":"string",
"title":"The Name Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"zhangsan"
]
},
"mobile":{
"$id":"#/properties/mobile",
"type":"string",
"title":"The Mobile Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"13688089999"
]
},
"hobby":{
"$id":"#/properties/hobby",
"type":"array",
"title":"The Hobby Schema",
"description":"An explanation about the purpose of this instance.",
"default":[
],
"items":{
"$id":"#/properties/hobby/items",
"type":"string",
"title":"The Items Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"Sing",
"Travel",
"Football"
]
}
},
"age":{
"$id":"#/properties/age",
"type":"integer",
"title":"The Age Schema",
"description":"An explanation about the purpose of this instance.",
"default":0,
"examples":[
30
]
},
"birthday":{
"$id":"#/properties/birthday",
"type":"string",
"title":"The Birthday Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"1990-02-22"
]
},
"address":{
"$id":"#/properties/address",
"type":"object",
"title":"The Address Schema",
"description":"An explanation about the purpose of this instance.",
"default":{
},
"examples":[
{
"street_address":"chun xi lu",
"city":"chengdu",
"country":"china"
}
],
"required":[
"street_address",
"city",
"country"
],
"properties":{
"street_address":{
"$id":"#/properties/address/properties/street_address",
"type":"string",
"title":"The Street_address Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"chun xi lu"
]
},
"city":{
"$id":"#/properties/address/properties/city",
"type":"string",
"title":"The City Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"chengdu"
]
},
"country":{
"$id":"#/properties/address/properties/country",
"type":"string",
"title":"The Country Schema",
"description":"An explanation about the purpose of this instance.",
"default":"",
"examples":[
"china"
]
}
}
}
}
}
3 什么是JSON Schema?
::: tip 定义
- JSON Schema是基于JSON格式,用于定义JSON数据结构以及校验JSON数据内容。
- JSON Schema规范:json-schema.org/draft-07/sc… :::
4 JSON Schema关键字汇总
Json Schema定义了一系列关键字,元数据通过这些关键字来描述Json数据的规范。其中有些关键字是通用的;有些关键字是针对特定类型的;还有些关键字是描述型的,不影响合法性校验。
| 关键字 | 描述 |
|---|---|
| $schema | 表示该JSON Schema文件遵循的规范,当前使用的schema版本 |
| $id | 为JSON模式声明一个唯一标识符,声明了一个解析*$ref* 的URI时的基础URI。 |
| title | 为该JSON Schema文件提供一个标题 |
| description | 关于该JSON Schema文件的描述信息 |
| format | 仅进行结构验证可能不足以验证实例是否满足应用程序的所有需求。 |
| default | 指定项的默认值。尽管许多JSON模式验证器会忽略这个关键字,但是在可能的情况下,JSON处理器可以使用此关键字的值为丢失的键值对提供默认值。 |
| examples | 这个关键字的值必须是一个数组,提供一组满足JSON模式的实例,用于向读者解释JSON模式的作用和效果。 |
| $comment | 这个关键字完全是为了用于向JSON模式源添加注释。此关键字的值必须是字符串。 |
| $ref | 用来引用同一个JSON模式文件或其他JSON模式文件中的JSON模式片段,这个属性的值通常是一个相对或者绝对URI。 |
| definitions | 可以在JSON模式中定义一个可以在这个JSON模式中被引用的JSON子模式 |
| type | 表示待校验元素的类型(例如,最外层的type表示待校验的是一个JSON对象,内层type分别表示待校验的元素类型为,整数,字符串,数字) |
| enum | 值必须是一个数组。这个数组应该至少有一个元素。数组中的元素应该是惟一的。 |
| multipleOf | 用于约束取值,值必须是一个严格大于0的数字。只有当数值实例除以这个关键字的值等于一个整数时才能验证通过。 |
| maximum | 用于约束取值范围,表示取值范围应该小于或等于maximum,只有当实例小于等于这个关键字的值时才能验证通过。 |
| minimum | 用于约束取值范围,表示取值范围应该大于或等于minimum |
| exclusiveMinimum | 如果minimum和exclusiveMinimum同时存在,且exclusiveMinimum的值为true,则表示取值范围只能大于minimum |
| exclusiveMaximum | 如果maximum和exclusiveMaximum同时存在,且exclusiveMaximum的值为true,则表示取值范围只能小于maximum |
| maxLength | 字符串类型数据的最大长度 |
| minLength | 字符串类型数据的最小长度 |
| pattern | 使用正则表达式约束字符串类型数据 |
| items | 该关键字的值必须是一个有效的JSON模式或一个有效的JSON模式数组。此关键字用来验证数组实例中的每个值,而不是直接验证实例本身。 |
| additionalItems | 此关键字的值必须是一个有效的JSON模式或者一个布尔值。此关键字用来验证数组中的元素,而不是直接验证直接实例本身。 |
| maxItems | 此关键字的值必须是一个非负整数。如果一个数组实例的大小小于等于此关键字的值,则验证通过。 |
| minItems | 此关键字的值必须是一个非负整数。如果一个数组实例的大小大于等于此关键字的值,则验证通过 |
| uniqueItems | 此关键字的值必须是一个布尔值。如果此关键字的值为布尔值 false,则数组实例验证通过 |
| contains | 此关键字的值必须是一个有效的JSON模式。如果一个数组实例中至少有一个元素能通过此关键字指定的JSON模式的验证,则验证通过。 |
| maxProperties | 此关键字的值必须是一个非负整数,如果一个对象的属性个数小于等于此关键字的值,则验证通过。 |
| minProperties | 此关键字的值必须是一个非负整数,如果一个对象的属性个数大于等于此关键字的值,则验证通过。 |
| required | 此关键字的值必须是一个数组。数组中的每一个元素(如果有的话)必须是一个字符串,并且必须是唯一的。如果此关键字的值中的每一个元素都是对象实例中某个属性的名称,则验证通过。 |
| properties | 定义待校验的JSON对象中,各个key-value对中value的限制条件.这个对象中的每一个值都必须是一个有效的JSON模式。 |
5 Json Schema 类型
5.1 Object
object类型有三个关键字:type,properties,required,如下:
| 关键字 | 描述 |
|---|---|
| type | 类型 |
| properties | 定义属性 |
| required | 必需属性 |
| maxProperties | 最大属性个数 |
| minProperties | 最小属性个数 |
| additionalProperties | true or false or object |
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
5.2 array
array有三个单独的属性:items,minItems,uniqueItems:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
| 关键字 | 描述 | 示例 |
|---|---|---|
| items | array 每个元素的类型 | . |
| minItems | 约束属性,数组最小的元素个数 | |
| maxItems | 约束属性,数组最大的元素个数 | |
| uniqueItems | 约束属性,每个元素都不相同 | |
| additionalProperties | 约束items的类型,不建议使用 | 示例 |
| Dependencies | 属性依赖 | 用法 |
| patternProperties | 用法 |
5.3 string
| 关键字 | 描述 |
|---|---|
| maxLength | 定义字符串的最大长度,>=0 |
| minLength | 定义字符串的最小长度,>=0 |
| pattern | 用正则表达式约束字符串 |
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"ip": {
"mail": "string",
"pattern":"w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*"
},
"host": {
"type": "phoneNumber",
"pattern":"((d{3,4})|d{3,4}-)?d{7,8}(-d{3})*"
},
},
"required": ["ip", "host"]
}
5.4 integer
| 关键字 | 描述 |
|---|---|
| minimum | 最小值 |
| exclusiveMinimum | 如果存在 "exclusiveMinimum" 并且具有布尔值 true,如果它严格意义上大于 "minimum" 的值则实例有效。 |
| maximum | 约束属性,最大值 |
| exclusiveMaximum | 如果存在 "exclusiveMinimum" 并且具有布尔值 true,如果它严格意义上小于 "maximum" 的值则实例有效。 |
| multipleOf | 是某数的倍数,必须大于0的整数 |
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "integer",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
5.5 number
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
5.6 boolean
{
"type": "object",
"properties": {
"number": { "type": "boolean" },
"street_name": { "type": "string" },
"street_type": { "type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
5.7 enum
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": ["Street", "Avenue", "Boulevard"]
}
}
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
}
}
}
6 子模式应用关键字
6.1 子模式的条件应用关键字
这些关键字协同工作,根据另一个子模式的结果实现子模式的条件应用。 这些关键字不能跨子模式边界相互交互。换句话说,allOf 中的一个分支中的 if 不能对另一个分支中的 then 或 else 产生影响。 当这些关键字不存在时,它们都没有默认行为。特别是,不能将它们视为带有空模式,当 if 不存在时,必须完全忽略 then 和 else。
| 关键字 | 描述 |
|---|---|
| if | 这个关键字指定的JSON模式的的验证结果对整个验证结果没有直接影响,而是用来控制 then 或 else 中的哪个关键字中的JSON模式被用来进行判断。 |
| then | 如果存在 if 关键字,并且实例成功通过了 if 关键字指定的JSON模式的验证,那么如果实例也成功通过了这个关键字指定的JSON模式的验证,那么则该关键字验证通过。 |
| else | 如果存在 if 关键字,而实例未能对其指定的JSON模式进行验证,那么如果实例成功通过了该关键字指定的JSON模式的验证,那么则该关键字验证通过 |
6.1.1 条件子模式举例说明
如果我们用如下的JSON模式来处理美国和加拿大的地址。这些国家的邮政编码的格式不一样,我们想要根据地址所处的国家来验证邮政编码的格式。如果是美国的地址,则邮政编码字段由5位数字和可选的4位数字后缀组成,如果是加拿大的地址,则邮政编码是固定的6位长度的数字和大写字母组合。
{
"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-Z0-9]{6}" } }
}
}
上面的例子只能处理两个国家的地址,如果要处理多个国家的地址,可以使用 allOf 关键字将 if-then 关键字组合起来使用,如下所示:
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"enum": ["US", "Canada", "Netherlands"]
}
},
"allOf": [
{
"if": {
"properties": { "country": { "const": "US" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
}
},
{
"if": {
"properties": { "country": { "const": "Canada" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[A-Z0-9]{6}" } }
}
},
{
"if": {
"properties": { "country": { "const": "Netherlands" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
}
}
]
}
6.2 使用布尔逻辑子模式的关键字
这些关键字的值必须是一个非空数组,并且数组中的每一个元素必须是一个有效的JSON模式。
| 关键字 | 描述 |
|---|---|
| allOf | 如果一个实例成功通过这个关键字指定的所有JSON模式,则验证通过 |
| anyOf | 如果一个实例成功通过了这个关键字指定的至少一个JSON模式的验证,则验证通过。 |
| oneOf | 如果一个实例成功通过了这个关键字指定的其中一个JSON模式的验证,则验证通过。 |
| not | 如果一个实例对于这个关键字指定的JSON模式验证失败,则验证通过。 |
6.2.1 布尔逻辑子模式示例
allOf
意思是展示全部属性,建议用requires替代, 不建议使用,示例如下
{
"definitions": {
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
},
"allOf": [
{ "$ref": "#/definitions/address" },
{ "properties": {
"type": { "enum": [ "residential", "business" ] }
}
}
]
}
anyOf
意思是展示任意属性,建议用
requires替代和minProperties替代。 我们用如下一个JSON模式验证一个实例是否能满足是字符串,值在"Ruby"、"Java"、"Swift"之中,值等于"Python"这三个条件中至少一个条件:
{
"anyOf": [
{"type": "string"},
{"enum": ["Ruby", "Java", "Swift"]},
{"const": "Python"},
{"type": "number" }
]
}
oneOf
我们用如下一个JSON模式验证一个实例是否是字符串、对象或者数组之一:
{
"oneOf": [
{"type": "string"},
{"type": "object"},
{"type": "array"},
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
not
非 * 类型,我们用如下的JSON模式表示不允许字符串实例:
{
"not": {"type": "string"}
}
7 验证语义的关键字 format
仅进行结构验证可能不足以验证实例是否满足应用程序的所有需求。定义
format关键字是为了允许对权威资源(无论是rfc还是其他外部规范)精确描述的固定值子集进行互操作语义验证。 这个关键字的值称为格式属性,必须是string类型。format属性通常只能验证给定的一组实例类型。如果要验证的实例类型不在此集合中,则此格式属性和实例的验证应该成功。 已定义的格式有如下几种,并且这些属性只应用于字符串类型的实例:
7.1 日期和时间
- date
- time
- datetime
7.2 电子邮件地址
- idn-email
7.3 主机名
- hostname
- idn-hostname
7.4 IP地址
- ipv4
- ipv6
7.5 资源标识符
- uri
- uri-reference
- iri
- iri-reference
7.6 URI模版
- uri-template
7.7 JSON指针
- json-pointer
- relative-json-pointer
7.8 正则表达式
- regex
7.9 验证format示例
{
"$id": "https://www.lsdcloud.com/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"minLength": 3
}
},
"required": [
"email"
],
"additionalProperties": false
}
8 JSON Schema进阶
了解了上面的各个类型的定义及约定条件,就可以满足大部分情况了。但为了写出更好的
json schema,我们再学习几个关键字
8.1 $ref
$ref 用来引用其它schema,
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product set",
"type": "array",
"items": {
"title": "Product",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "number"
},
"name": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"dimensions": {
"type": "object",
"properties": {
"length": {"type": "number"},
"width": {"type": "number"},
"height": {"type": "number"}
},
"required": ["length", "width", "height"]
},
"warehouseLocation": {
"description": "Coordinates of the warehouse with the product",
"$ref": "http://json-schema.org/geo"
}
},
"required": ["id", "name", "price"]
}
}
8.2 definitions
当一个
schema写的很大的时候,可能需要创建内部结构体,再使用$ref进行引用,示列如下:
{
"$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": { "$ref": "#/definitions/address" }
}
}
9 整体示例说明
{
# 该关键字用于指定JSON Schema版本: draft-07
"$schema": "http://json-schema.org/draft-07/schema#",
# 描述对应的JSON元素,title相对来说,更加简洁
"title": "book info",
# 描述对应的JSON元素,description更加倾向于详细描述相关信息
"description": "some information about book",
# 该关键字用于限定待校验JSON元素所属的数据类型,取值可为:object,array,integer,number,string,boolean,null
"type": "object",
# 用于指定JSON对象中的各种不同key应该满足的校验逻辑,
# 如果待校验JSON对象中所有值都能够通过该关键字值中定义的对应key的校验逻辑,每个key对应的值,都是一个JSON Schema,则待校验JSON对象通过校验。
"properties": {
"id": {
"description": "The unique identifier for a book",
"type": "integer",
"minimum": 1
},
"name": {
"description": "book name",
"type": "string",
"minLength": 3,
"maxLength": 30
},
"info": {
"description": "simple information about book",
"type": "string",
"minLength": 10,
"maxLength": 60
},
"tips": {
"anyOf": [ # 满足其中一个类型 就行
{"type": "string", "minLength": 10, "maxLength": 60},
{"type": "number", "minimum": 5.0}
]
},
"price": {
"description": "book price",
"type": "number",
# 能被0.5整除
"multipleOf": 0.5,
# 这里取等,5.0=<price<=99999.0
"minimum": 5.0,
"maximum": 99999.0,
# 若使用下面这两个关键字则 5.0<price<99999.0
# "exclusiveMinimum": 5.0,
# "exclusiveMaximum": 99999.0
},
"tags": {
"type": "array",
"items": [
{
"type": "string",
"minLength": 2,
"maxLength": 8
},
{
"type": "number",
"minimum": 1.0
}
],
# 待校验JSON数组第一个元素是string类型,且可接受的最短长度为5个字符,第二个元素是number类型,且可接受的最小值为10
# 剩余的其他元素是string类型,且可接受的最短长度为2。
"additonalItems": {
"type": "string",
"miniLength": 2
},
# 至少一个
"miniItems": 1,
# 最多5个
"maxItems": 5,
# 值为true时,所有元素都具有唯一性时,才能通过校验。
"uniqueItems": True
},
"date": {
"description": "书籍出版日期",
"type": "string",
# 可以是以下取值:date、date-time(时间格式)、email(邮件格式)、hostname(网站地址格式)、ipv4、ipv6、uri等。
# 使用format关键字时,在实例化validator时必须给它传format_checker参数,值如:draft7_format_checker, 网址:
# https://python-jsonschema.readthedocs.io/en/latest/validate/#jsonschema.Draft7Validator
"format": "date",
},
"bookcoding": {
"description": "书籍编码",
"type": "string",
# 符合该关键字指定的正则表达式,才算通过校验。
"pattern": "^[A-Z]+[a-zA-Z0-9]{12}$"
},
"other": {
"description": "其他信息",
"type": "object",
"properties": {
"info1": {
"type": "string"
},
"info2": {
"type": "string"
}
}
}
},
# 指定了待校验JSON对象可以接受的最少 一级key 的个数
"minProperties": 3,
# 指定了待校验JSON对象可以接受的最多 一级key 的个数。
"maxProperties": 7,
# patternProperties对象的每一个一级key都是一个正则表达式,value都是一个JSON Schema。
# 只有待校验JSON对象中的一级key,通过与之匹配的patternProperties中的一级正则表达式,对应的JSON Schema的校验,才算通过校验。
# 下面的JSON Schema表示, 所有以a开头的一级key的value都必须是number,
"patternProperties": {
"^a": {
"type": "number"
},
},
# 如果待校验JSON对象中存在,既没有在properties中被定义,又没有在patternProperties中被定义,那么这些一级key必须通过additionalProperties的校验。
"additionalProperties": {
"desc": {
"type": "string",
"minLength": 1
},
},
# 该关键字限制了JSON对象中必须包含哪些一级key。
# 如果一个JSON对象中含有required关键字所指定的所有一级key,则该JSON对象能够通过校验。
"required": ["id", "name", "info", "price"]
}