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