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