[How-to] 如何寫一份 JSON Schema

當要驗證 JSON 資料格式是否正確,有幾種做法

  1. 丟給後端去驗證
  2. 寫個 JSON Schema 做驗證(前端就可以先檢查了)

JSON Schema 是什麼?他有點像以前 XSD (用來描述 XML 結構的檔案,也有驗證的效果),除了可以用來驗證 JSON 資料格式是否正確,也可以用來驗證 YAML 檔案的格式。

基本語法

所以如何寫一個 JSON Schema,我們需要寫另外一份 JSON 來描述未來想要驗證的 JSON 資料格式為何,型態為何,可以輸入得資料有哪些等,下面就跟大家一起整理相關的語法筆記 (部分範例來自 JSON Schema 網站,連結在參考資料)

假設有一個 JSON 資料長這樣

1
2
3
4
5
6
{
"productId": 1,
"productName": "A green door",
"price": 12.50,
"tags": [ "home", "green" ]
}

一個基本的 JSON schema 長這樣

1
2
3
4
5
6
7
8
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/product.schema.json",
"title": "Product",
"description": "A product in the catalog",
"type": "object"
...
}
  • keyword
    • $schema: 參考的 JSON schema 是哪一個版本
    • $id: 這份 JSON schema 存放的位置
  • annotation
    • titledescription 描述說明此份 schema 的用途
  • validation keyword
    • type : 資料格式型態,可以描述的型態有 nullbooleanobjectarraynumberstringinteger

上面是設定跟說明這份 schema 的用途,類似 metadata,至於如何描述資料結構,描述方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"properties": {
"productId": {
"description": "The unique identifier for a product",
"type": "integer"
},
"productName": {
"description": "Name of the product",
"type": "string"
}
},
"required": [ "productId", "productName" ]
}
  • validation keyword
    • properties 用來描述此 Object 內有哪些欄位
    • required 用來設定哪些欄位是必填的

更多語法

1
2
3
4
5
"price": {
"description": "The price of the product",
"type": "number",
"exclusiveMinimum": 0
}
1
2
3
4
5
6
7
8
9
"tags": {
"description": "Tags for the product",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
  • 此為陣列型別的欄位
  • 陣列 內得資料格式,使用 items 定義
  • 至少要有一筆資料 (minItems 來設定最少筆數)
  • 且不能重複 (uniqueItems 檢查)

Nesting data structures

不難,一樣式套用上面的規則,做法是一樣的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"dimensions": {
"type": "object",
"properties": {
"length": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
}
},
"required": [ "length", "width", "height" ]
}

References schema

1
2
3
4
"warehouseLocation": {
"description": "Coordinates of the warehouse where the product is located.",
"$ref": "https://example.com/geographical-location.schema.json"
}
  • $ref 指定 schema 的參考路徑,可以是外部或內部 (spec)

    • 外部參考: 使用 URI Reference

    • 內部參考: 使用 #/路徑 的方式,搭配 $defs 使用

      image-20210606115417347

進階語法

  • additionalProperties: 是否有其他額外的欄位,false 時,JSON 得資料必須符合 schema 所定義的 properties
  • patternProperties: 用 Regex 來描述 Property 名稱規則及對應得資料格式
  • oneOf 符合定義規則的其中一項
    • MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
    • An instance validates successfully against this keyword if it validates successfully against exactly one schema defined by this keyword’s value.
  • $defs 搭配 $ref 使用 (舊版名稱為: definitions)
    • MUST be an object. Each member value of this object MUST be a valid JSON Schema.
    • reserves a location for schema authors to inline re-usable JSON Schemas into a more general schema.
  • enum: 設定可以使用的值有哪些
  • MUST be an array and at least one element.
  • An instance validates successfully against this keyword if its value is equal to one of the elements in this keyword’s array value.
  • pattern: 使用 Regex 設定可以輸入資料的格式 (format)

完整範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
{
"$id": "https://example.com/entry-schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "JSON Schema for an fstab entry",
"type": "object",
"required": [ "storage" ],
"properties": {
"storage": {
"type": "object",
"oneOf": [
{ "$ref": "#/$defs/diskDevice" },
{ "$ref": "#/$defs/diskUUID" },
{ "$ref": "#/$defs/nfs" },
{ "$ref": "#/$defs/tmpfs" }
]
},
"fstype": {
"enum": [ "ext3", "ext4", "btrfs" ]
},
"options": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
},
"uniqueItems": true
},
"readonly": {
"type": "boolean"
}
},
"$defs": {
"diskDevice": {
"properties": {
"type": {
"enum": [ "disk" ]
},
"device": {
"type": "string",
"pattern": "^/dev/[^/]+(/[^/]+)*$"
}
},
"required": [ "type", "device" ],
"additionalProperties": false
},
"diskUUID": {
"properties": {
"type": {
"enum": [ "disk" ]
},
"label": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
}
},
"required": [ "type", "label" ],
"additionalProperties": false
},
"nfs": {
"properties": {
"type": { "enum": [ "nfs" ] },
"remotePath": {
"type": "string",
"pattern": "^(/[^/]+)+$"
},
"server": {
"type": "string",
"oneOf": [
{ "format": "hostname" },
{ "format": "ipv4" },
{ "format": "ipv6" }
]
}
},
"required": [ "type", "server", "remotePath" ],
"additionalProperties": false
},
"tmpfs": {
"properties": {
"type": { "enum": [ "tmpfs" ] },
"sizeInMB": {
"type": "integer",
"minimum": 16,
"maximum": 512
}
},
"required": [ "type", "sizeInMB" ],
"additionalProperties": false
}
}
}

想要知道更多 JSON Schema 語法的寫法,可以到 JSON Schema Store 內去瞭解,裡面列出很多現在常用的服務,很多都是用來驗證 YAML 格式是否正確,或是閱讀這篇文件

參考資料

  1. JSON Schema
  2. JSON Schema specification
  3. JSON Schema Store