Lambdaカクテル

京都在住Webエンジニアの日記です

JSON Schemaメモ: subtypeのoneOfについて

JSON Schemaには,oneOfという便利な構文が用意されている。これは,与えられたスキーマのうちどれか1つだけがvalidなときvalidとする構文だ。

さて,そのoneOfの挙動で困ったのでメモする。具体的には,オブジェクト型Aと,別のオブジェクト型B,そしてAとBとをallOfでマージしたABとがあるとき,AとBとABとのoneOfを構成することができないのだ。 例として以下にスキーマを示す。このスキーマ自体はvalidだが,実は#abを許容できない。

{
    "definitions": {
        "a": {
            "$id": "#a",
            "additionalProperties": true,
            "type": "object",
            "required": ["a"],
            "properties": {
                "a": { "type": "number" }
            }
        },
        "b": {
            "$id": "#b",
            "additionalProperties": true,
            "type": "object",
            "required": ["b"],
            "properties": {
                "b": { "type": "number" }
            }
        },
        "ab": {
            "$id": "#ab",
            "additionalProperties": false,
            "properties": { "a": {}, "b": {} },
            "allOf": [{ "$ref": "#a" }, { "$ref": "#b" }]
        }
    },
    "$schema": "http://json-schema.org/draft-07/schema#",
    "oneOf": [{ "$ref": "#a" }, { "$ref": "#b" }, { "$ref": "#ab" }]
}

oneOfの挙動

どうして#abを許容しないかというと,oneOfの挙動にもとづいている。oneOfは,「与えられたスキーマでJSONをvalidateしていき,どれか1つのスキーマにだけvalidなときにvalidになる」構文だ。この例では,#a#b#abのうち,いずれか1つだけがvalidにならなければならない。 しかしながら,#abに対してvalidなJSONは#aにも#bにもvalidになってしまう(allOfで制約を満たしているのだから当然だが)ので,いずれか1つだけのスキーマにvalidになることができない。

大抵の場合,本当に欲しいのはoneOfではなく,「どれかにマッチする(anyOf)」なので,そう置換する。