CUBE SUGAR STORAGE

技術系のことかきます。
Recent Tweets @
Python のバリデータだと、以前このブログでも FormEncode を扱ったことがある。 今回は同様のパッケージとして jsonschema について書いてみる。

まず、元々の “jsonschema” というものは JSON の構造を JSON で記述するための仕様を指している。 そして、今回紹介する Python パッケージの jsonschema は、その仕様を元にバリデータを実装したものだ。 元々の “jsonschema” の仕様については以下の Web サイトを参照してもらいたい。
http://json-schema.org/

まずインストールは PyPI から。
$ pip install jsonschema

jsonschema のバリデーション・ルールは辞書と文字列で記述する。 詳しくは json-schema.org の仕様を確認すると良いが、以下のサンプルに目を通すだけでも雰囲気は掴めるはず。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import jsonschema

SCHEMA = {
    'type': 'object',
    'properties': {
        'name': {
            'type': 'string',
            'minLength': 1,
            'maxLength': 64,
        },
        'age': {
            'age': 'integer',
            'minimum': 0,
            'maximum': 100,
        },
    },
    'required': [
        'name',
        'age',
    ],
}

if __name__ == '__main__':
    import json

    ok = '''
    {
        "name": "Foo",
        "age": 15
    }
    '''
    ok_dict = json.loads(ok)
    jsonschema.validate(ok_dict, SCHEMA)

    ng = '''
    {
        "name": "Bar"
    }
    '''
    ng_dict = json.loads(ng)
    jsonschema.validate(ng_dict, SCHEMA)

もし jsonschema.validate() にルールから外れた辞書が入ると ValidationError が上がる。 例えば上記では、定義で ‘age’ が required (必須) になっているにも関わらず、変数 ng に代入した文字列は ‘age’ が無い。
Traceback (most recent call last):
  File "/Users/amedama/Documents/workspace/js1.py", line 44, in <module>
    jsonschema.validate(ng_dict, SCHEMA)
  File "/Users/amedama/.virtualenvs/py34/lib/python3.4/site-packages/jsonschema/validators.py", line 432, in validate
    cls(schema, *args, **kwargs).validate(instance)
  File "/Users/amedama/.virtualenvs/py34/lib/python3.4/site-packages/jsonschema/validators.py", line 117, in validate
    raise error
jsonschema.exceptions.ValidationError: 'age' is a required property

Failed validating 'required' in schema:
    {'properties': {'age': {'age': 'integer',
                            'maximum': 100,
                            'minimum': 0},
                    'name': {'maxLength': 64,
                             'minLength': 1,
                             'type': 'string'}},
     'required': ['name', 'age'],
     'type': 'object'}

On instance:
    {'name': 'Bar'}

もちろん、オブジェクトが複雑にネストした構造になっていてもバリデートできる。 例えば、以下では ‘emails’ にたどり着くまでに構造が ‘array’ -> ‘object’ -> ‘array’ -> ‘string’ と入れ子になっている。 尚、’array’ (配列) を扱う場合には ‘properties’ ではなく ‘items’ で中身を表す。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import jsonschema

SCHEMA = {
    'type': 'array',
    'items': {
        'type': 'object',
        'properties': {
            'name': {
                'type': 'string'
            },
            'emails': {
                'type': 'array',
                'items': {
                    'type': 'string',
                },
            },
        },
        'required': [
            'name',
            'emails',
        ],
    },
}

if __name__ == '__main__':
    import json

    ok = '''
    [
        {
            "name": "Foo",
            "emails": [
                "foo@example.jp"
            ]
        },
        {
            "name": "Bar",
            "emails": [
                "bar@example.com"
            ]
        }
    ]
    '''
    ok_dict = json.loads(ok)
    jsonschema.validate(ok_dict, SCHEMA)

FormEncode もそうだけど、一般的なバリデータは独自のクラスを組み合わせてルールを記述していくものが多い。 それに対し jsonschema はルールの記述が Python の辞書と文字列だけで行える点が魅力だ。 また、jsonschema を使ったバリデータは各言語に実装があるので、考え方やルールが使いまわせるのもありがたい。