pythonでjsonスキーマを使用する

# はじめに
JSON形式の通信データのやり取りをする際に、プログラムや装置に悪影響を及ぼさないために通信データのチェックをすると思います。
ただ、そのチェックを一から組むのは手間なのでチェックの設定とデータを与えるとチェックを実行してくれる仕組みが作られています。
チェックの設定はJSONスキーマ、チェック用のライブラリはpythonのjsonschemaを使用します。
JSONスキーマに関しては[公式のサイト](http://json-schema.org/)があります。


# 環境
 - python:3.6.5
 - jsonschema:3.0.1
  

# JSONスキーマによるチェック方法
JSON形式のデータをチェックするためのJSONスキーマを作成して、pythonに読み込みチェックする方法を記載します。

## JSONスキーマの説明の記載
直接的にデータのチェックには使いませんが、作成したJSONスキーマがどのようなものなのかを表す情報を記載します。

| key | 説明 |
|-----|------- |
| $schema | jsonスキーマのバージョン(例はdraft-7版) |
| title  | 名前 |
| description | メモ |


``` json:schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
}
```


## JSONスキーマのデータチェックの設定
データのチェックの設定を記載していきます。
色々な設定ができますが、詳細は次に説明することにして、ここでは簡単な例を記載します。


| key | 説明 |
|-----|------- |
| type | 型(例は連想配列と文字列と整数) |
| properties | json内の子要素(例はnameとageキー) |

``` json:schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "properties" :{
        "name": {
            "type": "string"
        },
        "age": {
            "type": "integer"
        }
    }
}
```


## pythonでjsonスキーマを読み込む
pythonのjsonschemaライブラリに使用するため、作成したjsonスキーマファイルを読み込みます。読み込んだjsonスキーマファイルはjsonライブラリでdict型にしておきます。


``` python:json_schema_main.py
import json

with open('schema.json') as file_obj:
    json_schema = json.load(file_obj)

```

## pythonでデータのチェックをする
読み込んだjsonスキーマファイルのdictとサンプルのdictをjsonschemaライブラリに読み込ませることでサンプルデータ(item)のチェックを行います。


``` python:json_schema_main.py
import json
from jsonschema import validate, ValidationError

with open('schema.json') as file_obj:
    json_schema = json.load(file_obj)

item = {
    "kind":"cat",
    "name": "tama",
}

try:
    validate(item, json_schema)
except ValidationError as e:
    print(e.message)

print('END')
```

結果

```
# python3 json_schema_main.py
END
```

itemの中の要素はnameがstringなのでチェック結果がOKになり、正常に終了します。
itemの中のnameを```"name": 12,```に変えて実施してみます。

結果

```
# python3 json_schema_main.py
12 is not of type 'string'
END
```

チェックが正常に動き、nameがstringではないとエラーが出ました。


## 入力必須なキーを指定する
実際に使用するときは必ずプログラムに必要なデータがあると思います。そのようなデータは必ずデータに入れてほしいため、必須キーとしてJSONスキーマの```required```に教えてあげることでチェック対象になります。
nameとageを必須キーとして設定しました。


``` json:schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "required": [
        "name",
        "age"
    ],
    "properties" :{
        "name": {
            "type": "string"
        },
        "age": {
            "type": "integer"
        }
    }
}
```

itemにageが含まれていないpythonを実行しました。

結果

```
# python3 json_schema_main.py
'age' is a required property
END
```

itemにageが含まれていないため、チェックが働き、ageが必須だという旨のメッセージが表示されました。
もちろんitemにageが含まれているときは何もエラーを出さずに終了します。


## 指定外キーをチェックする
プログラムによっては、不要なものは入力してほしくないケースがあります。そのような場合は、propertiesに設定したキー以外を許す設定```additionalProperties```をFalseにしてあげることで指定していないキーをチェックすることができます。
具体的には上のpythonのkindがそれにあたります。

``` json:schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "additionalProperties": false,
    "required": [
        "name",
        "age"
    ],
    "properties" :{
        "name": {
            "type": "string"
        },
        "age": {
            "type": "integer"
        }
    }
}
```

上と同じpythonを実行しました。

結果

```
# python3 json_schema_main.py
Additional properties are not allowed ('kind' was unexpected)
END
```

kindがjsonスキーマに含まれていないため、チェックが働き、kindが許可されていない旨のメッセージが表示されました。
もちろんitemにkindがいないときは何もエラーを出さずに終了します。

## おわりに
pythonでjsonスキーマを使用する方法について記載しました。とは言ってもpython要素はほぼなく、ほとんどjsonスキーマのお話になってしまいました。
jsonスキーマは、python特有のものではなく様々な言語で使用されているので汎用性が高いかなと思っています。
これ以外にも頑張れば色々なことがjsonスキーマでできるので機会があればまとめてみます。
[jsonスキーマを色々な機能を使用する](https://qiita.com/mink0212/items/b3036dc8ddfc60aa0ce8)にまとめてみました。