# JSONスキーマを使ってみる
###### tags: `json`
## JSONスキーマ
* [JSON Schema](https://json-schema.org/)
## JSONの整形
* [JSON formatter](https://jsonformatter.curiousconcept.com/)
* [Prettier.io](https://prettier.io/playground/)
## JSONスキーマの生成
* [JSONschema.Net](https://jsonschema.net/)
## JSONスキーマによる検証
* [JSONschema.Net](https://jsonschema.net/)
* [JSON Schema Validator](https://www.jsonschemavalidator.net/)
# JSONデータ
サンプルとなるJSONデータはこんな感じ。
```json=
{
"bookmarks": [
{
"url": "https://www1.example.com",
"title": "Title1",
"description": "Description1",
"datetime": "2020-01-01T11:11:11Z"
},
{
"url": "https://www2.example.com",
"title": "Title2",
"description": "Description2",
"datetime": "2020-02-02T22:22:22Z"
}
]
}
```
# JSONスキーマの生成
JSONデータから[JSONschema.Net](https://jsonschema.net/)でスキーマを生成する。このスキーマはJSONデータの構造をそのままなぞった構造になっている。
```json=
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/root.json",
"type": "object",
"title": "The Root Schema",
"required": [
"bookmarks"
],
"properties": {
"bookmarks": {
"$id": "#/properties/bookmarks",
"type": "array",
"title": "The Bookmarks Schema",
"items": {
"$id": "#/properties/bookmarks/items",
"type": "object",
"title": "The Items Schema",
"required": [
"url",
"title",
"description",
"datetime"
],
"properties": {
"url": {
"$id": "#/properties/bookmarks/items/properties/url",
"type": "string",
"title": "The Url Schema",
"default": "",
"examples": [
"https://www1.example.com"
],
"pattern": "^(.*)$"
},
"title": {
"$id": "#/properties/bookmarks/items/properties/title",
"type": "string",
"title": "The Title Schema",
"default": "",
"examples": [
"Title1"
],
"pattern": "^(.*)$"
},
"description": {
"$id": "#/properties/bookmarks/items/properties/description",
"type": "string",
"title": "The Description Schema",
"default": "",
"examples": [
"Description1"
],
"pattern": "^(.*)$"
},
"datetime": {
"$id": "#/properties/bookmarks/items/properties/datetime",
"type": "string",
"title": "The Datetime Schema",
"default": "",
"examples": [
"2020-01-01T11:11:11Z"
],
"pattern": "^(.*)$"
}
}
}
}
}
}
```
# JSONスキーマによるJSONデータの検証
## [JSON Schema validation online](https://json-schema-validator.herokuapp.com/)による検証
[JSON Schema validation online](https://json-schema-validator.herokuapp.com/)においてこのJSONデータをこのJSONスキーマにより検証してみると確かに成功する。`$id`とか`examples`などがunknownとかignoredとなっているのが気になる。
![JSONスキーマによる検証結果](https://i.imgur.com/sXIMVqQ.png)
## [JSON Schema Validator](https://www.jsonschemavalidator.net/)による検証
![](https://i.imgur.com/SftTUhL.png)
# JSONスキーマ自体の検証
JSONスキーマ自体もJSONであるから検証可能である。[JSON Schema Validator](https://www.jsonschemavalidator.net/)には有名どころのJSONスキーマがいくつか登録されており、その中にはJSONスキーマ自体のJSONスキーマも登録されている。生成されたJSONスキーマを検証すると成功する。ただしこれをもって正しく機能するJSONスキーマであるということはできない。例えば`$ref`で参照する先が無かったとしてもそのような誤りの有無は検証できない。
![](https://i.imgur.com/QCoVlko.png)
JSONスキーマのJSONスキーマを見ると`definitions`が宣言されている。`definitions`の代わりに`aiueo`と書いたとしてもそれが`$ref`で正しく参照されていればそのJSONスキーマを使って検証ができるのだが、やはり`definitions`を使うのが正しいようだ。このような点を厳しく検証したいならば、JSONスキーマのJSONスキーマにあえて `"additionalProperties":false` を指定すれば検証においてエラーとなる。
![](https://i.imgur.com/fSilzSZ.png)
# JSONスキーマでよく使う宣言
## 必須のプロパティ
オブジェクトが持つべき必須のプロパティは`required`で宣言する。自動的に作成されたJSONスキーマにもあるので特にこれ以上は書かない。
## 複数の型を取りうるプロパティ
`"type"="object"`とすればそのプロパティの取りうる値をオブジェクトに限定することを宣言する。`"type"=["object","null","boolean"]`などとすることもできる。JavaScriptと異なり、nullはオブジェクトではない。
## 追加のプロパティ
追加可能なプロパティを宣言するためには`additionalProperties`を使う。`"additionalProperties"=false`すればJSONスキーマで宣言しなかったプロパティを禁止することになる。
## スキーマ内での`$ref`
JSONスキーマの階層が深すぎるので、いくつかの宣言をJSONスキーマのルート直下の`definitions`プロパティ以下に移動し`$ref`で参照する。
```json=
{
"definitions": {
"url": {
"type": ["string", "null"]
},
"title": {
"type": ["string", "null"]
},
"description": {
"type": ["string", "null"]
},
"datetime": {
"type": ["string", "null"]
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bookmarks"],
"properties": {
"bookmarks": {
"type": "array",
"items": {
"type": "object",
"required": ["url", "title", "description", "datetime"],
"properties": {
"url": {
"$ref": "#/definitions/url"
},
"title": {
"$ref": "#/definitions/title"
},
"description": {
"$ref": "#/definitions/description"
},
"datetime": {
"$ref": "#/definitions/datetime"
}
},
"additionalProperties": false
}
}
}
}
```
`bookmarks`がとる値はオブジェクトのリストと宣言されているが、この宣言も`bookmark`として分離できる。
```json=
{
"definitions": {
"url": {
"type": ["string", "null"]
},
"title": {
"type": ["string", "null"]
},
"description": {
"type": ["string", "null"]
},
"datetime": {
"type": ["string", "null"]
},
"bookmark": {
"type": "object",
"required": ["url", "title", "description", "datetime"],
"properties": {
"url": {
"$ref": "#/definitions/url"
},
"title": {
"$ref": "#/definitions/title"
},
"description": {
"$ref": "#/definitions/description"
},
"datetime": {
"$ref": "#/definitions/datetime"
}
},
"additionalProperties": false
}
},
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bookmarks"],
"properties": {
"bookmarks": {
"type": "array",
"items": {
"$ref": "#/definitions/bookmark"
}
}
}
}
```
`$ref`を使ったJSONスキーマでも検証に成功する。
![](https://i.imgur.com/pHcmzUZ.png)
# 外部のJSONスキーマの`$ref`による参照
JSONスキーマが長くて読みにくいので、`definitions`を[gist](https://gist.github.com/TakashiSasaki/e118e9cfca7c35278dcff60d400dea69#file-url-title-description-datetine-json)に移動した。参照先には`definitions`以外のプロパティが無いのでルートに移動した。これに伴い相対URLであった`$ref`を絶対URLに変更した。URLはgistの[raw URL](https://gist.githubusercontent.com/TakashiSasaki/e118e9cfca7c35278dcff60d400dea69/raw/6cb94d2b832a268e58883f2c319261a053ac08b4/url-title-description-datetine.json)を使った。
{%gist TakashiSasaki/928eee5994217dfeea687d482338a70b %}
```json=
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["bookmarks"],
"properties": {
"bookmarks": {
"type": "array",
"items": {
"$ref": "https://gist.githubusercontent.com/TakashiSasaki/928eee5994217dfeea687d482338a70b/raw/external.json#/definitions/bookmark"
}
}
}
}
```
![](https://i.imgur.com/1dLbEAm.png)
# おまけ,空のJSONスキーマ
`{}`という空のJSONスキーマも立派(?)なJSONスキーマである。どんなJSONデータを検証しても成功する。
## [JSON Schema validation online](https://json-schema-validator.herokuapp.com/)による検証
![](https://i.imgur.com/Itt4XOp.png)
## [JSON Schema Validator](https://www.jsonschemavalidator.net/)による検証
![](https://i.imgur.com/dueIroQ.png)