LoginSignup
16
18

More than 3 years have passed since last update.

jsonスキーマの色々な機能を使用する

Last updated at Posted at 2019-09-09

はじめに

pythonでjsonスキーマを使用するでは、jsonスキーマの簡単な使用方法をまとめました。
jsonスキーマには、さらに色々な機能がありますが、あまり知られていないためまとめようと思います。そのため、ここにまとめてあるのはjsonスキーマでチェックできる型や上限など良く知られているものは省略しています。

環境

  • python:3.6.5
  • jsonschema:3.0.1

値チェックの色々

複数値内で値が合致しているものがあるかチェックする

入力値があらかじめ決まっている場合にenumに設定してあげることで設定した値が入力されたらOK、入力された値が設定外ならエラーになります。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "properties" :{
        "kind": {
            "type": "string",
            "enum": [
                "dog",
                "cat"
            ]
        }
    }
}

サンプル用の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": "mouse",
}

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

print('END')

結果

'mouse' is not one of ['dog', 'cat']
END

jsonスキーマでkindキーの値はdog、catだと指定しているため、mouseを入力するとエラーして、mouseはdogでもcatでもないという旨のメッセージが表示されました。

同一キーで複数のチェックパターンを設定する

あまり仕様的に褒められたものではないですが、同じキーを再利用して全く別の値を入力することがあります。その際に、anyOfを使用すると同じキーでありながら別パターンのチェックをすることができます。

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

サンプル用のpythonは上と同じです。
itemの値だけ変更しています。

結果

0.01 is not of type 'string'
END

jsonスキーマでkindキーの値は文字列と整数と指定しているため、0.01を入力するとエラーしました。また、当然ですが文字列と整数を入力した場合はエラーせずに終了しました。
今回は型の指定のみしましたが{}内に上限値など他の設定を入れるとその設定もそれぞれに有効になります。

複数パターンのキーをチェックする

anyOfの応用です。これもあまり仕様的に褒められたものではないですが、場合によって入力されるキーのパターンが複数ある場合にもanyOfを使うことでチェックできます。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "anyOf":[
            {"additionalProperties": false, "properties":{"cat": {"type": "string"}}},
            {"additionalProperties": false, "properties":{"dog": {"type": "string"}}}
    ]
}

サンプル用のpythonは上と同じです。
itemの値だけ変更しています。

結果

Additional properties are not allowed ('mouse' was unexpected)
END

jsonスキーマでキーがcatまたはdogと指定しているため、itemのキーをmouseにして入力するとエラーしました。また、当然ですがdogとcatのキーを入力した場合はエラーせずに終了しました。

あるパターンのキーをチェックする

キーとキーの間の整合性を取るために2つのうちどちらかのキーのみ入力してほしい場合があります。その場合にはoneOfを使うことでチェックできます。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "oneOf":[
            {"additionalProperties": false, "properties":{"cat": {"type": "string"}}},
            {"additionalProperties": false, "properties":{"dog": {"type": "string"}}}
    ]
}

サンプル用のpythonは上と同じです。
itemの値だけ変更しています。

json_schema_main.py
item = {
    "dog": "pochi",
    "cat": "tama"
}

結果

Additional properties are not allowed ('dog' was unexpected)
END

正常パターン

json_schema_main.py
item = {
    "dog": "pochi"
}

結果

END

jsonスキーマでキーがcatとdogのパターンを禁止しているため、itemにdogとcatを入力するとエラーしました。また、当然ですがcatのキーだけを入力した場合はエラーせずに終了しました。anyOfと同様にこれも様々な設定をできます。

指定した以外の値を受け取る

ある値以外を欲しいという場合にallOfnotを使用してチェックできます。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "properties":{
        "dog": {
            "allOf": [
                {"type": "string"},
                {"not": {"enum": ["pochi", "tama"]}}
            ]
        }
    }
}

サンプル用のpythonは上と同じです。
itemの値だけ変更しています。

json_schema_main.py
item = {
    "dog": "pochi"
}

結果

{'enum': ['pochi', 'tama']} is not allowed for 'pochi'
END

正常パターン

json_schema_main.py
item = {
    "dog": "tibi"
}

結果

END

enumでpochiとtamaだけの設定をnotで反転させ、pochiとtamaは受け付けないようにしました。また、allOfを使用して文字列と値の指定両方を満たすようにチェックしています。特にallOfを使用する必要はありませんが丁度良かったので使いました。

jsonスキーマの表記の色々

設定をリンクで指定する

設定が複雑になっていくと設定だけ別ファイルや別ブロックにまとめたい場合があります。その場合は、$refに指定してあげることでリンクすることができます。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "$ref": "#/definitions/dog",
    "definitions": {
        "dog": {
            "properties" :{
                "kind": {
                    "type": "string",
                    "enum": ["dog"]
                }
            }
        }
    }
}
item = {
    "kind": "cat",
}

結果

'cat' is not one of ['dog']
END

refで指定したdefinitions/dogの設定が反映されました。
上記例はあまり見やすくなった気がしないですが、anyOfやoneOfなど複雑になっていくとこの設定があるかないかで大きく見やすさが変わります。

設定を別ファイルに分ける

次は別ファイルに分けていきます。json側では$refにファイル名を指定してあげて、python側の設定で別ファイルのリンクを設定してあげます。
python側の設定はjsonschemaのライブラリのRefResolverクラスに設定ファイルがあるディレクトリと元のjsonスキーマを渡してあげてvalidate時に指定してあげる必要があります。

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "PyJsonValidate",
    "description": "sample json for json validate",
    "type": "object",
    "$ref": "dog.json"
}
dog.json
{
    "properties" :{
        "kind": {
            "type": "string",
            "enum": ["dog"]
        }
    }
}
json_schema_main.py
import os
import json
from jsonschema import validate, ValidationError, RefResolver

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


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

try:
    schema_path = 'file:///{0}/'.format((os.getcwd()).replace("\\", "/"))
    ref = RefResolver(schema_path, json_schema)
    validate(item, json_schema, resolver=ref)
except ValidationError as e:
    print(e.message)

print('END')

結果

'cat' is not one of ['dog']
END

jsonschemaライブラリの使い方が少しネックですが、jsonファイルはとても分かりやすくなりました。今回は設定まるまる別ファイルにしましたがやろうと思えばどんな設定も分けられるため、ルールを決めてファイル分けすると見やすくなるのではないかと思います。

おわりに

jsonスキーマの色々な使い方についてまとめました。おそらく、頭を使えばほとんどの入力値チェックがjsonスキーマで可能なほど汎用性のあるものでした。
ただ、ここまでやってしまうと人が見てわかりやすいjsonという利点をつぶしてしまいそうなので個人的にはよっぽどの理由がない限りは単純なjsonを使用しています。
開発の時に他の人の技術レベルを考えて開発していますが、技術レベルと開発のレベルはトレードオフの関係なので、どのレベルに抑えるのかがすごい悩みます。

16
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
18