{
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
type字段
type字段可以是一個字符串或一個數組
-
如果是一個字符串則表示基本數據類型,如:42、42.0
{ "type": "number" }
-
如果是一個字符串,則表示數據可以是其中的任一基本類型,如:
42
、"Life, the universe, and everything"
,但不能是結構化的數據類型,如:["Life", "the universe", "and everything"]
{ "type": ["number", "string"] }
jsonschema的五種基本類型
string
{ "type": "string" }
可以表示的字符串如:"This is a string"
、""
、"Déjà vu"
(unicode字符)
length
用於限制字符串的長度
{
"type": "string",
"minLength": 2,
"maxLength": 3
}
正則表達式
使用pattern
字段設置正則表達式,具體參見官方說明
{
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
數字類型
integer
{ "type": "integer" }
用於表示整數類型。需要注意的是,小數點的存在與否並不能判斷它是一個整數還是浮點數,例如1
和1.0
都會被認爲是整數,但3.1415926
則是浮點數。
number
用於表示任意數字類型,即整數或浮點數
{ "type": "number" }
multiples
用於表示特定數字的倍數,如下可以是0
、10
、20,但23
不是10的倍數,所以允許。
{
"type": "number",
"multipleOf" : 10
}
range
使用minimum
和maximum
表示的數字範圍(或使用exclusiveMinimum
和exclusiveMaximum
表示獨佔範圍)
- x ≥
minimum
- x >
exclusiveMinimum
- x ≤
maximum
- x <
exclusiveMaximum
如下可以表示0
,10
,99
,但-1
和100
、101
是錯誤的:
{
"type": "number",
"minimum": 0,
"exclusiveMaximum": 100
}
注意在JSON Schema Draft 4中exclusiveMinimum
和exclusiveMaximum
的工作方式並不相同,它們表示一個boolean值,用於判斷是否排除minimum
和maximum
值
if exclusiveMinimum is false, x ≥ minimum.
if exclusiveMinimum is true, x > minimum.
object
objects是JSON中的mapping類型,即將"keys"映射到"values","keys"必須是字符串,通常將每一對映射稱爲"屬性"。
{ "type": "object" }
可以表達如下值:
{
"key": "value",
"another_key": "another_value"
}
properties
屬性是object中使用properties
關鍵字定義的key-value對。properties
的值是一個對象,每個key的值作爲一個property的名稱,且每個值都用來校驗該屬性。任何與properties
的屬性名不匹配的屬性都將被忽略。
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
}
}
上述表達式可以匹配
-
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }
-
{ "number": 1600, "street_name": "Pennsylvania" }
-
{ }
-
{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
但不能匹配
-
{ "number": "1600", "street_name": "Pennsylvania", "street_type": "Avenue" }
Pattern Properties
有時候期望對於某一類屬性名稱,匹配一個特定的模式,此時可以使用patternProperties
:它使用正則表達式來進行模式匹配。如果一個屬性的名稱匹配到特定的正則表達式,則使用對於的模式來校驗該屬性的值。
如下表示使用S_
開頭的屬性必須是字符串類型,而使用 I_
開頭的則必須是整數類型,並忽略不匹配正則表達式的屬性。
{
"type": "object",
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
}
}
上述表達式可以匹配
-
{ "S_25": "This is a string" }
-
{ "I_0": 42 }
-
{ "keyword": "value" }
但不能匹配:
-
{ "S_0": 42 }
-
{ "I_42": "This is a string" }
Additional Properties
additionalProperties
關鍵字用於控制不在properties
關鍵字或不在patternProperties
正則表達式列表中的屬性。默認情況下允許這類properties。將additionalProperties
設置爲false
表示不允許額外的屬性。
如下表達式不允許{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": false
}
還可以使用非boolean對額外的屬性增加更加複雜的限制。如下表示進允許類型爲字符串的額外屬性,此時可以允許{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" }
,但不允許{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "office_number": 201 }
{
"type": "object",
"properties": {
"number": { "type": "number" },
"street_name": { "type": "string" },
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
},
"additionalProperties": { "type": "string" }
}
如下將additionalProperties
和properties
、patternProperties
結合起來使用,例如{ "keyword": "value" }
不匹配properties
和patternProperties
,但它匹配了additionalProperties
,因此允許該對象。
{
"type": "object",
"properties": {
"builtin": { "type": "number" }
},
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
},
"additionalProperties": { "type": "string" }
}
擴展封閉模式
需要注意由於additionalProperties
只能識別相同子模式的屬性,因此可能會限制使用Schema Composition關鍵字進行擴展。例如下述表達式本意是要求對象中包含"street_address", "city", "state"和"type"這幾個字段:
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"],
"additionalProperties": false
}
],
"properties": {
"type": { "enum": [ "residential", "business" ] }
},
"required": ["type"]
}
但對於下述對象,會因爲將"type"認爲是額外的屬性,而無法通過additionalProperties
的校驗
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business"
}
但下述對象又由於缺少"type"而無法通過required
的校驗
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC"
}
由於additionalProperties
只能識別相同子模式中的properties,它會將非"street_address", "city"和"state"的屬性認爲是額外的屬性,一種解決方案是將additionalProperties
轉移到擴展的模式中,並在擴展的模式中重新定義屬性
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
],
"properties": {
"street_address": true,//使用boolean表示必須出現該屬性
"city": true,
"state": true,
"type": { "enum": [ "residential", "business" ] }
},
"required": ["type"],
"additionalProperties": false
}
在 draft 2019-09可以使用unevaluatedProperties
關鍵字解決這種問題
Unevaluated Properties
unevaluatedProperties
關鍵字與additionalProperties
類似的,但它可以識別子模式的屬性。因此上述例子可以寫爲:
{
"allOf": [
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
],
"properties": {
"type": { "enum": ["residential", "business"] }
},
"required": ["type"],
"unevaluatedProperties": false
}
這樣就可以允許:
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business"
}
不允許:
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business",
"something that doesn't belong": "hi!"
}
unevaluatedProperties的工作原理是收集所有在處理模式時成功驗證的屬性,並將其作爲允許的屬性列表使用。下面例子中僅在"type"爲"business"時允許"department"屬性。
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"type": { "enum": ["residential", "business"] }
},
"required": ["street_address", "city", "state", "type"],
"if": {
"type": "object",
"properties": {
"type": { "const": "business" }
},
"required": ["type"]
},
"then": {
"properties": {
"department": { "type": "string" }
}
},
"unevaluatedProperties": false
}
上述表達式允許:
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "business",
"department": "HR"
}
不允許:
{
"street_address": "1600 Pennsylvania Avenue NW",
"city": "Washington",
"state": "DC",
"type": "residential",
"department": "HR"
}
Required Properties
默認情況下,properties
關鍵字中的屬性不是必須的,但可以通過required
關鍵字指定需要的屬性。
required
關鍵字可以指定0或多個字符串數組,每個字符串都必須唯一。如下表達式要求對象中有"name"和"email"屬性。
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": { "type": "string" },
"telephone": { "type": "string" }
},
"required": ["name", "email"]
}
注意上述表達式不允許如下對象,這是因爲null的類型不是"string",而是"null":
{
"name": "William Shakespeare",
"address": "Henley Street, Stratford-upon-Avon, Warwickshire, England",
"email": null
}
屬性名稱
New in draft 6
屬性的名稱可以根據模式進行驗證,而不考慮它們的值。如下強制所有的名稱必須是有效的ASCII 字符
{
"type": "object",
"propertyNames": {
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
}
}
由於對象的keys必須是字符串,這也意味着propertyNames
的模式至少是:
{ "type": "string" }
size
properties的數目可以使用minProperties
和maxProperties
進行限制,值爲整數。
{
"type": "object",
"minProperties": 2,
"maxProperties": 3
}
array
表示一組有序的元組,數組中可以包含不同類型的元素。
{ "type": "array" }
允許:
-
[1, 2, 3, 4, 5]
-
[3, "different", { "types" : "of values" }]
JSON使用了兩種數組方式:
- List validation: 任意長度的數組,每個元素都使用相同的模式
- Tuple validation: 固定長度的數組,每個元素都有可能使用不同的模式
item
List validation下使用item
關鍵字來校驗數組中的元素
{
"type": "array",
"items": {
"type": "number"
}
}
如上表達式允許數組[1, 2, 3, 4, 5]
、[]
,但不允許[1, 2, "3", 4, 5]
Tuple 校驗
假設爲了表達一個地址:"1600 Pennsylvania Avenue NW"。該地址是一個4元組[number, street_name, street_type, direction]
其中:
number
: 地址號碼,必須是數字street_name
: 街區名稱,必須是字符串street_type
: 街區類型,必須來自一組固定的字符串值direction
: 城市象限,必須來自一組固定的字符串值
爲了實現上述目的,需要使用prefixItems
關鍵字,prefixItems
表示一個數組,每個元素即一個模式,對應文檔數組的相應索引,即第一個元素校驗輸入數組的第一個元素,第二個元素校驗輸入數組的第二個元素。
在 Draft 4 - 2019-09中,使用
items
關鍵字的另一種形式來進行元組驗證。當items
是一個多模式數組是,它的行爲和prefixItems
相同。
實現上述目的的表達式如下:
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
]
}
允許:
-
[1600, "Pennsylvania", "Avenue", "NW"]
-
[10, "Downing", "Street"] //前三個元素
-
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
不允許:
-
["Palais de l'Élysée"] //第一個元素不是數字
-
[24, "Sussex", "Drive"] //第三個元素不匹配
額外的元素
可以使用items
關鍵字控制是否允許出現prefixItems
中定義的元組之外的元素。
如果將items
設置爲false
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
],
"items": false
}
將不允許:
-
[1600, "Pennsylvania", "Avenue", "NW", "Washington"] //包含額外元素
允許:
-
[1600, "Pennsylvania", "Avenue"]
可以使用非boolean的模式表示更復雜的限制,表示可以添加那些額外的元素:
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }
],
"items": { "type": "string" }
}
此時允許:
-
[1600, "Pennsylvania", "Avenue", "NW", "Washington"]
但不允許:
-
[1600, "Pennsylvania", "Avenue", "NW", 20500] //20500不是字符串
Contains
New in draft 6
contains
關鍵字要求數組中至少出現一個特定模式的元素
如下不允許出現["life", "universe", "everything", "forty-two"]
這種不帶任何數字的數組:
{
"type": "array",
"contains": {
"type": "number"
}
}
minContains / maxContains
New in draft 2019-09
和contains
關鍵字配合使用,限制contains
的模式次數。
{
"type": "array",
"contains": {
"type": "number"
},
"minContains": 2,
"maxContains": 3
}
不允許:
-
["apple", "orange", 2]
-
["apple", "orange", 2, 4, 8, 16]
length
使用minItems
和maxItems
限制數組的長度
{
"type": "array",
"minItems": 2,
"maxItems": 3
}
Uniqueness
將uniqueItems
設置爲true
,確保數組中元素的唯一性
{
"type": "array",
"uniqueItems": true
}
將不允許:
-
[1, 2, 3, 3, 4]
boolean
{ "type": "boolean" }
需要注意true
和false
要小寫
null
{ "type": "null" }
需要注意的是,在JSON中null
並不代表某些內容不存在
通用關鍵字
Annotations
JSON Schema中有一些關鍵字,這些關鍵字不用於校驗,僅用於描述模式,這類"註釋"關鍵字並不是必須的,但建議在實踐中使用,由此可以實現模式的"自文檔"。
title
和description
關鍵字必須是字符串。
default
關鍵字指定了默認值,該值不會填充驗證過程中缺失的值。一些非驗證的工具,如文檔生成器或格式生成器會使用該值來提示用戶如何使用一個值。
New in draft 6:examples
關鍵字提供了一組校驗模式的例子,它並不用於校驗,僅幫助讀者解釋模式的影響和目的。examples
中不需要default
,可以將default
看作是另一個examples
New in draft 7:通常會在API上下文中使用boolean類型的readOnly
和writeOnly
關鍵字,前者表示不可修改某個值,當使用PUT
請求修改值時,會響應400 Bad Request
。writeOnly
表示可以設置值,但將保持隱藏狀態,即可以通過PUT
請求設置一個值,但在無法通過GET
請求檢索到該值。
New in draft 2019-09 :deprecated
關鍵字用來表示未來將會移除該實例值。
{
"title": "Match anything",
"description": "This is a schema that matches anything.",
"default": "Default value",
"examples": [
"Anything",
4035
],
"deprecated": true,
"readOnly": true,
"writeOnly": false
}
Comments
New in draft 7 $comment
$comment
關鍵字用於給模式添加註釋,該值必須是字符串。
Enumerated values
enum
關鍵字用於指定一組固定的值。它必須是一個數組,且最少包含一個元素,每個元素都是唯一的。
{
"enum": ["red", "amber", "green", null, 42]
}
Constant values
New in draft 6
const
關鍵字用於指定單個值。如下例,將country
限制爲"United States of America"
,不允許出現其他值:
{
"properties": {
"country": {
"const": "United States of America"
}
}
}
Media: 字符串編碼的非JSON數據
JSON Schema中有一組關鍵字用於描述和選擇性校驗保存在JSON字符串中的非JSON數據。由於很難爲所有媒體類型編寫校驗器,因此JSON 模式校驗器不需要基於這些關鍵字驗證JSON字符串的內容。但對於那些需要消費經過校驗的JSON的應用來說非常有用。
contentMediaType
contentMediaType
關鍵字用於指定字符串內容的MIME類型,參見 RFC 2046。IANA正式註冊了一系列MIME類型,但具體支持的類型將取決於應用程序和操作系統。
{
"type": "string",
"contentMediaType": "text/html"
}
可以允許"<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head></html>"
contentEncoding
contentEncoding
關鍵字指定保存內容的編碼類型,參見RFC 2054, part 6.1和RFC 4648。
可接受的值爲7bit
, 8bit
, binary
, quoted-printable
, base16
, base32
和 `base64,如果沒有指定,則與JSON文檔的編碼相同。
通常的用法如下:
- 如果編碼的內容和JSON文檔相同,則無需指定
contentEncoding
,按原樣將內容包含在字符串中即可。包含基於文本的類型,如text/html
或application/xml
- 如果內容是二進制,將
contentEncoding
設置爲base64
,並使用Base64進行編碼,這類包含很多媒體類型,如image/png
或音頻類型,如audio/mpeg
.
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/png"
}
可以允許"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA..."
模式組合
JSON Schema中有一些關鍵字可以用於將模式組合到一起。注意,這並意味着它們會組合來自多個文件或JSON樹的模式(儘管這些功能有助於實現這一點),更多參見構建複雜模式。組合模式可能很簡單,比如允許同時根據多個標準校驗一個值。
這些關鍵字對應於衆所周知的布爾代數概念,如AND、OR、XOR和NOT。你可以使用這些關鍵字來表達標準JSON Schema關鍵字無法表達的複雜限制。
這些關鍵字爲:
- allOf: (AND) 必須通過所有子模式的校驗
- anyOf: (OR) 必須通過任一個子模式的校驗
- oneOf: (XOR) 必須只能通過某一個子模式的校驗
- not: (NOT) 不能通過給定模式的校驗
allOf
{
"anyOf": [
{ "type": "string", "maxLength": 5 },
{ "type": "number", "minimum": 0 }
]
}
可以允許:
-
"short"
-
12
不允許:
-
"too long"
-
-5
oneOf
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
可以允許:
-
10
-
9
不允許:
-
2
-
15 // 同時是3和5的倍數
not
{ "not": { "type": "string" } }
允許:
-
42
-
{ "key": "value" }
不允許:
-
"I am a string"
模式組合的特點
不合邏輯的模式
如下組合是不符合邏輯的,因爲數據不可能既是字符串又是數字:
{
"allOf": [
{ "type": "string" },
{ "type": "number" }
]
}
分解模式
可以將"因子"放到子模式的公共部分之外,如下兩種模式是等價的:
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
{
"type": "number",
"oneOf": [
{ "multipleOf": 5 },
{ "multipleOf": 3 }
]
}
子模式條件
dependentRequired
dependentRequired
關鍵字要求當對象中出現給定的屬性時,要求出現特定的屬性。例如,如果你有信用卡號,則必須保證還有一個賬單地址,發明之如果沒有信用卡號,那麼也不需要賬單地址了。使用dependentRequired
關鍵字可以表示一個屬性對其他屬性的依賴關係。dependentRequired
關鍵字的值是一個對象,對象中的每個條目會映射到屬性的名稱。
如下,當提供了credit_card
屬性時,也必須出現billing_address
屬性:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" },
"billing_address": { "type": "string" }
},
"required": ["name"],
"dependentRequired": {
"credit_card": ["billing_address"]
}
}
允許:
-
{ "name": "John Doe", "credit_card": 5555555555555555, "billing_address": "555 Debtor's Lane" }
-
{ "name": "John Doe" //沒有提供credit_card,不產生依賴 }
-
{ "name": "John Doe", "billing_address": "555 Debtor's Lane" //billing_address並沒有任何依賴 }
不允許:
-
{ "name": "John Doe", "credit_card": 5555555555555555 }
dependentSchemas
dependentSchemas
關鍵字要求當出現給定的屬性時,應用特定的子模式。下面表示當出現credit_card
時,要求出現billing_address
,且billing_address
必須是字符串
{
"type": "object",
"properties": {
"name": { "type": "string" },
"credit_card": { "type": "number" }
},
"required": ["name"],
"dependentSchemas": {
"credit_card": {
"properties": {
"billing_address": { "type": "string" }
},
"required": ["billing_address"]
}
}
}
允許:
-
{ "name": "John Doe", "credit_card": 5555555555555555, "billing_address": "555 Debtor's Lane" }
-
{ "name": "John Doe", "billing_address": "555 Debtor's Lane" //不存在credit_card }
If-Then-Else
New in draft 7,與編程語言中的if
/then
/else
類似。
if | then | else | whole schema |
---|---|---|---|
T | T | n/a | T |
T | F | n/a | F |
F | n/a | T | T |
F | n/a | F | F |
n/a | n/a | n/a | T |
例如,如果你想編寫一個模式來處理United States 和Canada的地址,這兩個國家的郵政編碼格式不同,我們需要根據不同的國家來進行校驗。如果地址在United States,則postal_code
字段爲zipcode:5位數字,後面跟4位可選的數字後綴。如果地址在Canada,則postal_code
字段爲6位字母數字串。
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"default": "United States of America",
"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-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
}
允許:
-
{ "street_address": "1600 Pennsylvania Avenue NW", "country": "United States of America", "postal_code": "20500" }
-
{ "street_address": "1600 Pennsylvania Avenue NW", "postal_code": "20500" }
-
{ "street_address": "24 Sussex Drive", "country": "Canada", "postal_code": "K1M 1M4" }
不允許:
-
{ "street_address": "24 Sussex Drive", "country": "Canada", "postal_code": "10000" }
-
{ "street_address": "1600 Pennsylvania Avenue NW", "postal_code": "K1M 1M4" }
上例中並沒有要求出現"country"屬性,因此如果未定義"country"屬性,默認行爲會將"postal_code"驗證爲美國郵政編碼。“default”關鍵字沒有效果(只作提示作用)
上述方式只能處理兩個國家的情況,如果要處理多個國家,可以將多個if
和then
成對包含到allOf
中。
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"default": "United States of America",
"enum": ["United States of America", "Canada", "Netherlands"]
}
},
"allOf": [
{
"if": {
"properties": { "country": { "const": "United States of America" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
}
},
{
"if": {
"properties": { "country": { "const": "Canada" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
},
{
"if": {
"properties": { "country": { "const": "Netherlands" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
}
}
]
}
允許:
-
{ "street_address": "1600 Pennsylvania Avenue NW", "country": "United States of America", "postal_code": "20500" }
-
{ "street_address": "1600 Pennsylvania Avenue NW", "postal_code": "20500" }
-
{ "street_address": "24 Sussex Drive", "country": "Canada", "postal_code": "K1M 1M4" }
-
{ "street_address": "Adriaan Goekooplaan", "country": "Netherlands", "postal_code": "2517 JX" }
不允許:
-
{ "street_address": "24 Sussex Drive", "country": "Canada", "postal_code": "10000" }
-
{ "street_address": "1600 Pennsylvania Avenue NW", "postal_code": "K1M 1M4" }
上述"if"模式中的
required
字段是必須的,如果沒有該字段,則會將該模式作爲默認模式執行。例如,對於如下語句:{ "street_address": "1600 Pennsylvania Avenue NW", "postal_code": "K1M 1M4" }
如果按照上述表達式執行,結果爲:
Message:JSON does not match all schemas from 'allOf'. Invalid schema indexes: 0. Schema path: #/allOf Message:JSON does not match schema from 'then'. Schema path:#/allOf/0/then/then Message:String 'K1M 1M4' does not match regex pattern '[0-9]{5}(-[0-9]{4})?'. Schema path:#/allOf/0/then/properties/postal_code/pattern
如果去掉所有的
required
字段,則會將所有if
模式作爲默認模式進行匹配校驗,結果如下:Message:JSON does not match all schemas from 'allOf'. Invalid schema indexes: 0, 2. Schema path:#/allOf Message:JSON does not match schema from 'then'. Schema path:#/allOf/2/then/then Message:String 'K1M 1M4' does not match regex pattern '[0-9]{4} [A-Z]{2}'. Schema path:#/allOf/2/then/properties/postal_code/pattern Message:JSON does not match schema from 'then'. Schema path:#/allOf/0/then/then Message:String 'K1M 1M4' does not match regex pattern '[0-9]{5}(-[0-9]{4})?'. Schema path:#/allOf/0/then/properties/postal_code/pattern
implication
可以使用模式組合關鍵字來表示"if-then"條件,
{
"type": "object",
"properties": {
"restaurantType": { "enum": ["fast-food", "sit-down"] },
"total": { "type": "number" },
"tip": { "type": "number" }
},
"anyOf": [
{
"not": {
"properties": { "restaurantType": { "const": "sit-down" } },
"required": ["restaurantType"]
}
},
{ "required": ["tip"] }
]
}
允許:
-
{ "restaurantType": "sit-down", "total": 16.99, "tip": 3.4 }
-
{ "restaurantType": "fast-food", "total": 6.99 }
-
{ "total": 5.25 }
不允許:
-
{ "restaurantType": "sit-down", //不滿足anyOf "total": 16.99 }
聲明一個Dialect
一個JSON Schema版本稱爲一個Dialect,Dialect表示用於評估模式的一組關鍵字和語義。每個發佈的JSON Schama都是一個新的Dialect。
$schema
$schema
關鍵字用於聲明JSON Schema的dialect。$schema
關鍵字的值也是模式的標識符,可用於根據$schema
標識的dialect 驗證模式是否有效。描述另一個模式的模式稱爲"meta-schema"。
$schema
位於整個文檔的根,它不適用於外部引用的($ref
,$dynamicRef)文檔。
- Draft 4:
http://json-schema.org/draft-04/schema#
- Draft 6:
http://json-schema.org/draft-06/schema#
. - Draft 7:
http://json-schema.org/draft-07/schema#
. - Draft 2019-09:
https://json-schema.org/draft/2019-09/schema
.
Guidelines
可以使用Meta-data關鍵字提供幫助信息,因爲這類字段並不會影響校驗過程。
{
"type": "object",
"requiredProperties": {
"foo": { "type": "string" }
}
}
允許:
-
{ "foo": "bar" }
-
{ "foo": 42 } //無法識別requiredProperties字段
構造複雜的模式
本章介紹如何使用工具來重用和構造模式。
Schema Identification
與其他編程語言類似,如果將模式分爲多個邏輯單元,那麼就可以互相引用。爲了引用一個模式,需要一種方式來標識一個模式,稱爲non-relative URIs。
標識並不是必須的,只有在需要引用時纔會用到標識。無標識的模式稱爲"匿名模式"。
URI術語有時可能不直觀。在本文件中,使用了以下定義。
雖然使用URL來標識模式,但但這些標識並不需要網絡可達。
基本URI
使用非相對URI可能會很麻煩,因此JSON模式中使用的所有URI都可能是URI引用,它們會根據模式的基本URI進行解析,從而生成非相對URI。本節描述如何確定模式的基本URI。
RFC-3986中定義了基本URI和相對引用解析。
檢索URI
用於獲取模式的URI稱爲“檢索URI”。
假設使用URI引用了一個模式https://example.com/schemas/address
,然後檢索到以下模式。
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
此時該模式的基本URI與檢索URI相同
$id
可以在模式的根使用$id
關鍵字定義基本URI,$id
的值是一個URI引用,沒有根據檢索URI解析的片段。
假設URIs https://example.com/schema/address
和 https://example.com/schema/billing-address
都使用瞭如下模式:
{
"$id": "/schemas/address",
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
無論使用兩個URI中的哪一個來檢索此模式,基本URI都是https://example.com/schemas/address
,這是$id
根據檢索URI解析出的結果。
然而,在設置基本URI時使用相對引用可能會有問題。例如,不能將此模式用作匿名模式,由於沒有檢索URI,且無法對任何內容解析相對引用。出於這種原因,建議在使用$id
聲明基本URI時,使用完整的URI。
{
"$id": "https://example.com/schemas/address",
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
JSON指針
除了表示一個模式文檔,還可以標識子模式。最常見的方式是在指向該子模式的URI片段中使用JSON 指針 。
JSON指針描述了一個斜槓分隔的路徑,用於遍歷文檔中對象中的鍵。/properties/street_address
意味着:
- 找到第一個鍵
properties
的值 - 在該對象中找到鍵
street_address
的值
URI https://example.com/schemas/address#/properties/street_address
標識了下述模式的含註釋的子模式
{
"$id": "https://example.com/schemas/address",
"type": "object",
"properties": {
"street_address":
{ "type": "string" }, //標識該子模式
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
$anchor
一種不太常見的識別子模式的方法是使用$anchor
關鍵字在模式中創建一個命名錨點,並在URI片段中使用該名稱。錨點必須以字母開頭,後跟任意數量的字母、數字-
, _
, :
或.
。
URI https://example.com/schemas/address#street_address
標識了下述模式的含註釋的子模式
{
"$id": "https://example.com/schemas/address",
"type": "object",
"properties": {
"street_address":
{
"$anchor": "street_address",//標識該子模式
"type": "string" //標識該子模式
},
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"]
}
$ref
一個模式可以使用$ref
關鍵字引用另一個模式。 $ref
是一個根據基本URI解析的URI引用。
假設需要定義一個客戶記錄,每個客戶都可能有一個送貨地址和賬單地址。地址格式是相同的,都有一個街區地址、城市和國家。
$ref
中的URL引用根據基本URI (https://example.com/schemas/customer
)解析爲 https://example.com/schemas/address
.
{
"$id": "https://example.com/schemas/customer",
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"shipping_address": { "$ref": "/schemas/address" },
"billing_address": { "$ref": "/schemas/address" }
},
"required": ["first_name", "last_name", "shipping_address", "billing_address"]
}
假設在匿名模式中使用
$ref
,則無法解析相對引用。如下例中,/properties/shipping_address
的$ref
可以正常解析,但/properties/billing_address
的$ref
則無法正常解析{ "type": "object", "properties": { "first_name": { "type": "string" }, "last_name": { "type": "string" }, "shipping_address": { "$ref": "https://example.com/schemas/address" }, "billing_address": { "$ref": "/schemas/address" } }, "required": ["first_name", "last_name", "shipping_address", "billing_address"] }
$def
$defs
關鍵字提供了一個標準化的位置來保存子模式,以便在當前模式文檔中重用。
擴展前面的客戶模式示例,爲name
屬性使用公共模式。爲此定義一個新的模式是沒有意義的,它只會在該模式中使用,因此可以選擇使用$defs
。
{
"$id": "https://example.com/schemas/customer",
"type": "object",
"properties": {
"first_name": { "$ref": "#/$defs/name" },
"last_name": { "$ref": "#/$defs/name" },
"shipping_address": { "$ref": "/schemas/address" },
"billing_address": { "$ref": "/schemas/address" }
},
"required": ["first_name", "last_name", "shipping_address", "billing_address"],
"$defs": {
"name": { "type": "string" }
}
}
$ref
不僅僅有助於避免重複。它還可以用於編寫更易於閱讀和維護的模式。可以使用帶有描述性名稱的$defs
來定義模式的複雜部分,並在需要的地方引用。
可以引用外部子模式,但通常將
$ref
限制爲引用外部模式或$defs
中定義的內部子模式。
遞歸
$ref
關鍵字可以爲指向的模式創建遞歸模式。例如,person
模式中有一個children
數組,而每個數組元素又是一個person
實例:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
"items": { "$ref": "#" }
}
}
}
允許:
-
{ "name": "Elizabeth", "children": [ { "name": "Charles", "children": [ { "name": "William", "children": [ { "name": "George" }, { "name": "Charlotte" } ] }, { "name": "Harry" } ] } ] }
上面創建了一個指向自身的模式,有效地在校驗器中創建了一個“循環”。但需要注意,如下,在$ref
引用另一個$ref
可能會在解析器中導致無限循環。
{
"$defs": {
"alice": { "$ref": "#/$defs/bob" },
"bob": { "$ref": "#/$defs/alice" }
}
}
Bundling
在子模式中使用$id
時,它表示一個嵌入的模式,它的標識符是$id
的值,該值根據它出現在其中的模式的基本URI進行解析。包含嵌入模式的模式文檔稱爲複合模式文檔。複合模式文檔中每個帶有$id
的模式稱爲模式資源。
本例顯示了捆綁到複合模式文檔中的客戶模式示例和地址模式示例:
{
"$id": "https://example.com/schemas/customer",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"shipping_address": { "$ref": "/schemas/address" },
"billing_address": { "$ref": "/schemas/address" }
},
"required": ["first_name", "last_name", "shipping_address", "billing_address"],
"$defs": {
"address": {
"$id": "/schemas/address",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "$ref": "#/definitions/state" }
},
"required": ["street_address", "city", "state"],
"definitions": {
"state": { "enum": ["CA", "NY", "... etc ..."] }
}
}
}
}
無論模式資源是否捆綁,複合模式文檔中的所有引用都必須相同。注意,客戶模式中的$ref
關鍵字沒有變更。唯一的區別是,地址模式現在定義爲/$defs/address
,而不是單獨的模式文檔。你無法使用#/$defs/address
引用地址模式,因爲如果將模式拆分,該引用將不再指向地址模式。
此外還可以看到“$ref”:“#/definitions/state”
解析爲地址模式中的definitions
關鍵字,而不是頂層模式中的definitions
關鍵字,就像不使用嵌入模式時一樣。
每個模式資源都是獨立評估的,可以使用不同的JSON模式dialects。上面的示例中,地址模式資源使用Draft 7,而客戶模式資源使用Draft 2020-12。如果嵌入式模式中沒有聲明$schema
,則默認使用父模式的dialects。