什么是Json Schema??以一個(gè)例子來(lái)說(shuō)明
假設(shè)有一個(gè)web api,接受一個(gè)json請(qǐng)求,返回某個(gè)用戶在某個(gè)城市關(guān)系最近的若干個(gè)好友。一個(gè)請(qǐng)求的例子如下:
{
? ? "city" : "chicago",
? ?"number": 20,
? ? "user" : {
? ? ? ? "name":"Alex",
? ? ? ? "age":20? ? ? ? }
}
在上面的例子中,web api要求提供city,number,user三個(gè)成員,其中city是字符串,number是數(shù)值,user是一個(gè)對(duì)象,又包含了name和age兩個(gè)成員。
對(duì)于api來(lái)說(shuō),需要定義什么樣的請(qǐng)求合法,即什么樣的Json對(duì)于api來(lái)說(shuō)是合法的輸入。這個(gè)規(guī)范可以通過Json Schema來(lái)描述,對(duì)應(yīng)的Json Schema如下。
{
? ? "type": "object",
? ??"properties": {
?? ?? ? "city": { "type": "string" },
? ??? ??"number": { "type": "number" },
?? ??? ?"user": {
?? ??? ?? ? "type": "object",
? ??? ??? ? "properties": {
?? ??? ??? ?? ? "name" : {"type": "string"},
?? ??? ??? ??? ?"age" : {"type": "number"}
?? ??? ??? ?}?? ??? ??? ??? ??? ?? ?
?? ??? }
? ? }
}
例子可以通過Json Schema Validator來(lái)驗(yàn)證。
1.什么是Json Schema
Json Schema定義了一套詞匯和規(guī)則,這套詞匯和規(guī)則用來(lái)定義Json元數(shù)據(jù),且元數(shù)據(jù)也是通過Json數(shù)據(jù)形式表達(dá)的。Json元數(shù)據(jù)定義了Json數(shù)據(jù)需要滿足的規(guī)范,規(guī)范包括成員、結(jié)構(gòu)、類型、約束等。
本文后面的部分是簡(jiǎn)要介紹Json Schema定義的這些規(guī)則,以及如何用這些規(guī)則描述規(guī)范。
Json Schema定義了一系列關(guān)鍵字,元數(shù)據(jù)通過這些關(guān)鍵字來(lái)描述Json數(shù)據(jù)的規(guī)范。其中有些關(guān)鍵字是通用的;有些關(guān)鍵字是針對(duì)特定類型的;還有些關(guān)鍵字是描述型的,不影響合法性校驗(yàn)。本文的主要內(nèi)容就是介紹這些關(guān)鍵字的應(yīng)用。
2. 類型關(guān)鍵字
首先需要了解的是"type"關(guān)鍵字,這個(gè)關(guān)鍵字定義了Json數(shù)據(jù)需要滿足的類型要求。"type"關(guān)鍵字的用法如下面幾個(gè)例子:
{"type":"string"}。規(guī)定了Json數(shù)據(jù)必須是一個(gè)字符串,符合要求的數(shù)據(jù)可以是
"Today is a good day."
"I love you"
{"type" : "object"}。規(guī)定了Json數(shù)據(jù)必須是一個(gè)對(duì)象,符合要求的數(shù)據(jù)可以是
{"name" : "Alexander", "age" : 98}
{}
{"type" : "number"}。規(guī)定了Json數(shù)據(jù)必須是一個(gè)數(shù)值,符合要求的數(shù)據(jù)可以是。Java Script不區(qū)分整數(shù)、浮點(diǎn)數(shù),但是Json Schema可以區(qū)分。
2
0.5
{"type": "integer"}。要求數(shù)據(jù)必須是整數(shù)。
2
{"type" : "array"}。規(guī)定了Json數(shù)據(jù)必須是一個(gè)數(shù)組,符合要求的數(shù)據(jù)可以是
["abc", "cdf"]
[1, 2, 3]
["abc", 25, {"name": "Alexander"} ]
[]
{"type" : "boolean"}。這個(gè)Json Schema規(guī)定了Json數(shù)據(jù)必須是一個(gè)布爾,只有兩個(gè)合法值
true
false
{"type" : "null"}。null類型只有一個(gè)合法值
null
3. 簡(jiǎn)單類型
這部分介紹類型特定的關(guān)鍵,包括字符串、數(shù)值、布爾、空值幾種基本類型。
3.1 字符串
Json合法的字符串?? ?? ? ?? ?
"Today is a good day."
對(duì)應(yīng)的Json Schema
{"type": "string"}
可以進(jìn)一步對(duì)字符串做規(guī)范要求。字符串長(zhǎng)度、匹配正則表達(dá)式、字符串格式。
3.1.1 字符串長(zhǎng)度
關(guān)鍵字: minLength, maxLength
可以對(duì)字符串的最小長(zhǎng)度、最大長(zhǎng)度做規(guī)范。
{
? ? "type" : "string",
? ? "minLength" : 2,
? ? "maxLength" : 3,
}
3.1.2 正則表達(dá)式
關(guān)鍵字: pattern
可以對(duì)字符串應(yīng)滿足的Pattern做規(guī)范,Pattern通過正則表達(dá)式描述。
3.1.3 字符串Format
關(guān)鍵字: format
可以通過Json Schema內(nèi)建的一些類型,對(duì)字符串的格式做規(guī)范,例如電子郵件、日期、域名等。
{ "type" : "string", "format" : "date", }
Json Schema支持的format包括"date", "time", "date-time", "email", "hostname"等。具體可以參考文檔。
3.2 數(shù)值
Json Schema數(shù)值類型包括"number"和"integer"。number合法的數(shù)值可以是
2
0.1
對(duì)應(yīng)的Json Schema為
{"type": "number"}
如果是integer則只能是整數(shù)。"number"和"integer"的類型特定參數(shù)相同,可以限制倍數(shù)、范圍。
3.2.1 數(shù)值滿足倍數(shù)
關(guān)鍵字: multipleOf
可以要求數(shù)值必須某個(gè)特定值的整數(shù)倍。例如要求數(shù)值必須是10的整數(shù)倍。
{
? ? "type" : "number",
? ? "multipleOf" : 10,
}
3.2.2 數(shù)值范圍
關(guān)鍵字: minimum, maximum, exclusiveMinimum, exclusiveMaximum
可以限制數(shù)值的方位,包括最大值、最小值、開區(qū)間最大值、開區(qū)間最小值。
要求數(shù)值在[0, 100)范圍內(nèi)。
{
? ? "type" : "number",
? ? "minimum": 0,
? ? "exclusiveMaximum": 100
}
3.3 布爾
布爾類型沒有額外的類型特定參數(shù)。
3.4 空值
null類型沒有額外的類型特定參數(shù)。
4. 復(fù)合類型
復(fù)合類型可以通過Nest的方式構(gòu)建復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。包括數(shù)組、對(duì)象。
4.1 數(shù)組
Json數(shù)組合法數(shù)據(jù)的例子
[1, 2, 3]
[1, "abc", {"name" : "alex"}]
[]
Json Schema為
{"type": "array"}
數(shù)組的類型特定參數(shù),可以用來(lái)限制成員類型、是否允許額外成員、最小元素個(gè)數(shù)、最大元素個(gè)數(shù)、是否允許元素重復(fù)。
4.1.1 數(shù)組成員類型
關(guān)鍵字: items
可以要求數(shù)組內(nèi)每個(gè)成員都是某種類型,通過關(guān)鍵字items實(shí)現(xiàn)。下面的Schema要求數(shù)組內(nèi)所有元素都是數(shù)值,這時(shí)關(guān)鍵字"items"對(duì)應(yīng)一個(gè)嵌套的Json Schema,這個(gè)Json Schema定義了每個(gè)元素應(yīng)該滿足的規(guī)范。
{
? ? "type": "array",
? ? "items": {
? ? ? ? "type": "number"? ? }
}
[1, 2, 3]
關(guān)鍵字items還可以對(duì)應(yīng)一個(gè)數(shù)組,這時(shí)Json數(shù)組內(nèi)的元素必須與Json Schema內(nèi)items數(shù)組內(nèi)的每個(gè)Schema按位置一一匹配。
{
? ? "type": "array",
? ? "items": [
? ? {
? ? ? ? "type": "number"? ? },
? ? {
? ? ? ? "type": "string"? ? }]
}
[1, "abc"]
4.1.2 數(shù)組是否允許額外成員
關(guān)鍵字: additionalItems
當(dāng)使用了items關(guān)鍵字,并且items關(guān)鍵字對(duì)應(yīng)的是Schema數(shù)組,這個(gè)限制才起作用。關(guān)鍵字additionalItems規(guī)定Json數(shù)組內(nèi)的元素,除了一一匹配items數(shù)組內(nèi)的Schema外,是否還允許多余的元組。當(dāng)additionalItems為true時(shí),允許額外元素。
{
? ? "type": "array",
? ? "items": [
? ? {
? ? ? ? "type": "number"? ? },
? ? {
? ? ? ? "type": "string"? ? }],
? ? "additionalItems" : true
}
[1, "abc", "x"]
4.1.3 數(shù)組元素個(gè)數(shù)
關(guān)鍵字: minItems, maxItems
可以限制數(shù)組內(nèi)元素的個(gè)數(shù)。
{
? ? "type": "array",
? ? "items": {
? ? ? ? "type": "number"? ? },
? ? "minItems" : 5,
? ? "maxItems" : 10
}
[1,2,3,4,5,6]
4.1.4 數(shù)組內(nèi)元素是否必須唯一
關(guān)鍵字: uniqueItems
規(guī)定數(shù)組內(nèi)的元素是否必須唯一。
{
? ? "type": "array",
? ? "items": {
? ? ? ? "type": "number"? ? },
? ? "uniqueItems" : true
}
[1,2,3,4,5]
4.2 對(duì)象
Json對(duì)象是最常見的Json數(shù)據(jù)類型,合法的數(shù)據(jù)可以是
{
? ? "name": "Froid",
? ? "age" : 26,
? ? "address" : {
? ? ? ? "city" : "New York",
? ? ? ? "country" : "USA"? ? }
}
就對(duì)象類型而言,最基本的類型限制Schema是
{"type" : "object"}
然而,除了類型外,我們通常需要對(duì)其成員做進(jìn)一步約定。對(duì)象的類型特定關(guān)鍵字,大多是為此目的服務(wù)的。
4.2.1 成員的Schema
關(guān)鍵字:properties
規(guī)定對(duì)象各成原所應(yīng)遵循的Schema。
{
? ? "type": "object", ? ??
? ? "properties": { ?? ?
? ? ? ? "name": {"type" : "string"},
? ? ? ? "age" : {"type" : "integer"},
? ? ? ? "address" : {
? ? ? ? ? ? "type" : "object",
? ? ? ? ? ? "properties" : {
? ? ? ? ? ? ? ? "city" : {"type" : "string"},
? ? ? ? ? ? ? ? "country" : {"type" : "string"}
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
對(duì)于上例中的Schema,合法的data是
{
? ? "name": "Froid",
? ? "age" : 26,
? ? "address" : {
? ? ? ? "city" : "New York",
? ? ? ? "country" : "USA"? ? }
}
properties關(guān)鍵字的內(nèi)容是一個(gè)key/value結(jié)構(gòu)的字典,其key對(duì)應(yīng)Json數(shù)據(jù)中的key,其value是一個(gè)嵌套的Json Schema。表示Json數(shù)據(jù)中key對(duì)應(yīng)的值所應(yīng)遵守的Json Schema。在上面的例子中,"name"對(duì)應(yīng)的Schema是{"type" : "string"},表示"name"的值必須是一個(gè)字符串。在Json數(shù)據(jù)中,對(duì)象可以嵌套,同樣在Json Schema中也可以嵌套。如"address"字段,在Json Schema中它的內(nèi)容是一個(gè)嵌套的object類型的Json Schema。
4.2.2 批量定義成員Schema
關(guān)鍵字:patternProperties
與properties一樣,但是key通過正則表達(dá)式匹配屬性名。
{
? ? "type": "object",
? ? "patternProperties": {
? ? ? ? "^S_": { "type": "string" },
? ? ? ? "^I_": { "type": "integer" }
? ? }
}
{"S_1" : "abc"}
{"S_1" : "abc", "I_3" : 1}
4.2.3 必須出現(xiàn)的成員
關(guān)鍵字:required
規(guī)定哪些對(duì)象成員是必須的。
{
? ? "type": "object", ? ??
? ? "properties": { ?? ?
? ? ? ? "name": {"type" : "string"},
? ? ? ? "age" : {"type" : "integer"},
? ? },
? ? "required" : ["name"]
}
上例中"name"成員是必須的,因此合法的數(shù)據(jù)可以是
{"name" : "mary", "age" : 26}
{"name" : "mary"}
但缺少"name"則是非法的
{"age" : 26}
4.2.4 成員依賴關(guān)系
關(guān)鍵字:dependencies
規(guī)定某些成員的依賴成員,不能在依賴成員缺席的情況下單獨(dú)出現(xiàn),屬于數(shù)據(jù)完整性方面的約束。
{
? ? "type": "object",
? ? "dependencies": {
? ? ? ? "credit_card": ["billing_address"]
? ? }
}
dependencies也是一個(gè)字典結(jié)構(gòu),key是Json數(shù)據(jù)的屬性名,value是一個(gè)數(shù)組,數(shù)組內(nèi)也是Json數(shù)據(jù)的屬性名,表示key必須依賴的其他屬性。
上面Json Schema合法的數(shù)據(jù)可以是
{}
{"billing_address" : "abc"}
但如果有"credit_card"屬性,則"billing_address" 屬性不能缺席。下面的數(shù)據(jù)是非法的
{"credit_card": "7389301761239089"}
4.2.5 是否允許額外屬性
關(guān)鍵字:additionaProperties
規(guī)定object類型是否允許出現(xiàn)不在properties中規(guī)定的屬性,只能取true/false。
{
? ? "type": "object", ? ??
? ? "properties": { ?? ?
? ? ? ? "name": {"type" : "string"},
? ? ? ? "age" : {"type" : "integer"},
? ? },
? ? "required" : ["name"],
? ? "additionalProperties" : false
}
上例中規(guī)定對(duì)象不能有"name"和"age"之外的成員。合法的數(shù)據(jù)
{"name" : "mary"}
{"name" : "mary", "age" : 26}
非法的數(shù)據(jù)
{"name" : "mary", "phone" : ""84893948}
4.2.6 屬性個(gè)數(shù)的限制
關(guān)鍵字:minProperties, maxProperties
規(guī)定最少、最多有幾個(gè)屬性成員。
{
? ? "type": "object",
? ? "minProperties": 2,
? ? "maxProperties": 3
}
{"name" : "mary", "age" : 26}
{"name" : "mary", "age" : 26, "phone" : "37839233"}
5. 邏輯組合
關(guān)鍵字:allOf, anyOf, oneOf, not
從關(guān)鍵字名字可以看出其含義,滿足所有、滿足任意、滿足一個(gè)。前三個(gè)關(guān)鍵字的使用形式是一致的,以allOf為例說(shuō)明其形式。
{
? ? "allOf" : [
? ? ? ? Schema1,
? ? ? ? Schema2,
? ? ? ? ...
? ? ]
}
其中,"allOf"的內(nèi)容是一個(gè)數(shù)組,數(shù)組內(nèi)的成員都是內(nèi)嵌的Json Schema。上例Schema1、Schema2都是內(nèi)嵌的Json Schema。整個(gè)Schema表示當(dāng)前Json數(shù)據(jù),需要同時(shí)滿足Schema1、Schema2,。
5.1 allOf
滿足allOf數(shù)組中的所有Json Schema。
{
? ? "allOf" : [
? ? ? ? Schema1,
? ? ? ? Schema2,
? ? ? ? ...
? ? ]
}
需要注意,不論在內(nèi)嵌的Schema里還是外部的Schema里,都不應(yīng)該使"additionalProperties"為false。否則可能會(huì)生成任何數(shù)據(jù)都無(wú)法滿足的矛盾Schema。
可以用來(lái)實(shí)現(xiàn)類似“繼承”的關(guān)系,例如我們定義了一個(gè)Schema_base,如果想要對(duì)其進(jìn)行進(jìn)一步修飾,可以這樣來(lái)實(shí)現(xiàn)。
{
? ? "allOf" : [
? ? ? ? Schema_base
? ? ]
? ? "properties" : {
? ? ? ? "other_pro1" : {"type" : "string"},
? ? ? ? "other_pro2" : {"type" : "string"}
? ? },
? ? "required" : ["other_pro1", "other_pro2"]
}
Json數(shù)據(jù)既需要滿足Schema_base,又要具備屬性"other_pro1"、"other_pro2"。
5.2 anyOf
滿足anyOf數(shù)組中的任意個(gè)Schema。
{
? ? "anyOf" : [
? ? ? ? Schema1,
? ? ? ? Schema2,
? ? ? ? ...
? ? ]
}
Json數(shù)據(jù)需要滿足Schema1、Schema2中的一個(gè)或多個(gè)。
5.3 oneOf
滿足且進(jìn)滿足oneOf數(shù)組中的一個(gè)Schema,這也是與anyOf的區(qū)別。
{
? ? "oneOf" : [
? ? ? ? Schema1,
? ? ? ? Schema2,
? ? ? ? ...
? ? ]
}
5.4 not
這個(gè)關(guān)鍵字不嚴(yán)格規(guī)定Json數(shù)據(jù)應(yīng)滿足什么要求,它告訴Json不能滿足not所對(duì)應(yīng)的Schema。
{
? ? "not" : {"type" : "string"}
}
只要不是string類型的都Json數(shù)據(jù)都可以。
6. 復(fù)雜結(jié)構(gòu)
對(duì)復(fù)雜結(jié)構(gòu)的支持包括定義和引用??梢詫⑾嗤慕Y(jié)構(gòu)定義成一個(gè)“類型”,需要使用該“類型”時(shí),可以通過其路徑或id來(lái)引用。
6.1 定義
關(guān)鍵字:無(wú)
定義一個(gè)類型,并不需要特殊的關(guān)鍵字。通常的習(xí)慣是在root節(jié)點(diǎn)的definations下面,定義需要多次引用的schema。definations是一個(gè)json對(duì)象,key是想要定義的“類型”的名稱,value是一個(gè)json 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" }
? ? }
}
上例中定義了一個(gè)address的schema,并且在兩個(gè)地方引用了它,#/definitions/address表示從根節(jié)點(diǎn)開始的路徑。
6.2?id>??關(guān)鍵字:id>??關(guān)鍵字:id**
可以在上面的定義中加入id屬性,這樣可以通過id屬性,這樣可以通過id屬性的值對(duì)該schema進(jìn)行引用,而不需要完整的路徑。
... "address": {
? ? ? ? ? ? "type": "object",
? ? ? ? ? ? "$id" : "address",
? ? ? ? ? ? "properties": {
? ? ? ? ? ? ? ? "street_address": { "type": "string" },
? ? ? ? ? ? ? ? "city":? ? ? ? ? { "type": "string" },
? ? ? ? ? ? ? ? "state":? ? ? ? ? { "type": "string" }
? ? ? ? ? ? },
? ? ? ? ? ? "required": ["street_address", "city", "state"]
? ? ? ? }
...
6.3 引用
關(guān)鍵字:$ref
關(guān)鍵字$ref可以用在任何需要使用json schema的地方。如上例中,billing_address的value應(yīng)該是一個(gè)json schema,通過一個(gè)$ref替代了。
$ref的value,是該schema的定義在json中的路徑,以#開頭代表根節(jié)點(diǎn)。
{
? ? "properties": {
? ? ? ? "billing_address": { "$ref": "#/definitions/address" },
? ? ? ? "shipping_address": { "$ref": "#/definitions/address" }
? ? }
}
如果schema定義了$id屬性,也可以通過該屬性的值進(jìn)行引用。
{
? ? "properties": {
? ? ? ? "billing_address": { "$ref": "#address" },
? ? ? ? "shipping_address": { "$ref": "#address" }
? ? }
}
7. 通用關(guān)鍵字
通用關(guān)鍵字可以在任何json schema中出現(xiàn),有些影響合法性校驗(yàn),有些只是描述作用,不影響合法性校驗(yàn)。
7.1 enum
關(guān)鍵字:enum
可以在任何json schema中出現(xiàn),其value是一個(gè)list,表示json數(shù)據(jù)的取值只能是list中的某個(gè)。
{
? ? "type": "string",
? ? "enum": ["red", "amber", "green"]
}
上例的schema規(guī)定數(shù)據(jù)只能是一個(gè)string,且只能是"red"、"amber"、"green"之一。
7.2 metadata
關(guān)鍵字:title,description,default,example
只作為描述作用,不影響對(duì)數(shù)據(jù)的校驗(yàn)。
{
? ? "title" : "Match anything",
? ? "description" : "This is a schema that matches anything.",
? ? "default" : "Default value",
? ? "examples" : [
? ? ? ? "Anything",
? ? ? ? 4035? ? ]
}