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屬性,這樣可以通過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"之一。
7.2 metadata
關鍵字:title,description,default,example
{
"title" : "Match anything", "description" : "This is a schema that matches anything.", "default" : "Default value", "examples" : [ "Anything", 4035 ] }
只作為描述作用,不影響對數據的校驗。