
JSON Schema で dependentRequired を使うと JSON のプロパティ構造を「条件付きで」バリデーションできる🔐 具体例を挙げると,任意プロパティが a と b 2つあるときに「a を指定する場合は b もセットで必要」というバリデーションをしたいときに使える❗️
JSON Schema のドキュメント (Conditional schema validation) に載っている例だと,name プロパティは必須で,もし任意の credit_card プロパティを指定する場合は billing_address プロパティもセットで必要という感じ👌
{ "type": "object", "properties": { "name": { "type": "string" }, "credit_card": { "type": "number" }, "billing_address": { "type": "string" } }, "required": ["name"], "dependentRequired": { "credit_card": ["billing_address"] } }
動作確認
pytest でテストコードを実装して dependentRequired の動作確認をしてみた❗️ちなみに JSON Schema のバージョンによって記法が違っていて,Draft 2020-12(正確には Draft 2019-09 以降)では dependentRequired で,Draft-07 だと dependencies となる.詳しくは Draft 2019-09 のリリースノートに載っている📝
テスト項目としては以下の3種類の JSON を JSON Schema Draft 2020-12 と JSON Schema Draft-07 でバリデーションしてみた👌
- INPUT_1(
name)🙆♂️ - INPUT_2(
nameandcredit_card)🙅 - INPUT_3(
nameandcredit_cardandbilling_address)🙆♂️
import jsonschema import pytest from faker import Faker fake = Faker() SCHEMA_DRAFT_2020_12 = { '$schema': 'https://json-schema.org/draft/2020-12/schema', 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'credit_card': {'type': 'number'}, 'billing_address': {'type': 'string'}, }, 'required': ['name'], 'dependentRequired': {'credit_card': ['billing_address']}, } SCHEMA_DRAFT_07 = { '$schema': 'https://json-schema.org/draft-07/schema', 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'credit_card': {'type': 'number'}, 'billing_address': {'type': 'string'}, }, 'required': ['name'], 'dependencies': {'credit_card': ['billing_address']}, } INPUT_1 = { 'name': fake.name(), } INPUT_2 = { 'name': fake.name(), 'credit_card': int(fake.credit_card_number()), } INPUT_3 = { 'name': fake.name(), 'credit_card': int(fake.credit_card_number()), 'billing_address': fake.address(), } def idfn(param): return param['id'] @pytest.mark.parametrize( 'patterns', [ { 'id': 'Draft 2020-12|1', 'schema': SCHEMA_DRAFT_2020_12, 'validator': jsonschema.Draft202012Validator, 'input': INPUT_1, 'valid': True, }, { 'id': 'Draft 2020-12|2', 'schema': SCHEMA_DRAFT_2020_12, 'validator': jsonschema.Draft202012Validator, 'input': INPUT_2, 'valid': False, }, { 'id': 'Draft 2020-12|3', 'schema': SCHEMA_DRAFT_2020_12, 'validator': jsonschema.Draft202012Validator, 'input': INPUT_3, 'valid': True, }, { 'id': 'Draft-07|1', 'schema': SCHEMA_DRAFT_07, 'validator': jsonschema.Draft7Validator, 'input': INPUT_1, 'valid': True, }, { 'id': 'Draft-07|2', 'schema': SCHEMA_DRAFT_07, 'validator': jsonschema.Draft7Validator, 'input': INPUT_2, 'valid': False, }, { 'id': 'Draft-07|3', 'schema': SCHEMA_DRAFT_07, 'validator': jsonschema.Draft7Validator, 'input': INPUT_3, 'valid': True, }, ], ids=idfn, ) def test_json_schema(patterns): if patterns['valid']: patterns['validator'](patterns['schema']).validate(patterns['input']) else: with pytest.raises(jsonschema.ValidationError): patterns['validator'](patterns['schema']).validate(patterns['input'])
実行すると期待通りになっていた👌
$ uv run pytest --verbose (中略) test_main.py::test_json_schema[Draft 2020-12|1] PASSED test_main.py::test_json_schema[Draft 2020-12|2] PASSED test_main.py::test_json_schema[Draft 2020-12|3] PASSED test_main.py::test_json_schema[Draft-07|1] PASSED test_main.py::test_json_schema[Draft-07|2] PASSED test_main.py::test_json_schema[Draft-07|3] PASSED
まとめ
JSON Schema の dependentRequired は便利そうだから覚えておこう〜 \( 'ω')/