リファレンス
サブスキーマを条件付きで適用する
dependentRequired
dependentRequired キーワードは、オブジェクト内に特定のプロパティが存在する場合、特定のプロパティが存在することを条件付きで要求します。たとえば、顧客を表すスキーマがあるとします。クレジットカード番号がある場合は、請求先住所も確実に入手したいでしょう。クレジットカード番号がない場合は、請求先住所は必須ではありません。プロパティ間のこのような依存関係を、dependentRequiredキーワードを使用して表します。dependentRequiredキーワードの値はオブジェクトです。オブジェクト内の各エントリは、プロパティ名pから、pが存在する場合に必要なプロパティをリストする文字列の配列にマッピングされます。
次の例では、credit_cardプロパティが提供されるたびに、billing_addressプロパティも存在する必要があります
"properties": { "name": { "type": "string" }, "credit_card": { "type": "number" }, "billing_address": { "type": "string" } },
"required": ["name"],
"dependentRequired": { "credit_card": ["billing_address"] }}
このインスタンスは、credit_cardを持っていますが、billing_addressがありません。
これは、credit_cardもbilling_addressもないため、問題ありません。
依存関係は双方向ではないことに注意してください。クレジットカード番号なしで請求先住所を持つことは問題ありません。
上記の問題(依存関係が双方向でない)を解決するには、もちろん、双方向の依存関係を明示的に定義できます。
"properties": { "name": { "type": "string" }, "credit_card": { "type": "number" }, "billing_address": { "type": "string" } },
"required": ["name"],
"dependentRequired": { "credit_card": ["billing_address"], "billing_address": ["credit_card"] }}
このインスタンスにはcredit_cardがありますが、billing_addressが欠落しています。
これにはbilling_addressがありますが、credit_cardが欠落しています。
dependentRequiredとdependentSchemasは、dependenciesという1つのキーワードでした。依存関係の値が配列の場合、dependentRequiredのように動作し、依存関係の値がスキーマの場合、dependentSchemaのように動作していました。dependentSchemas
dependentSchemas キーワードは、特定のプロパティが存在する場合に、サブスキーマを条件付きで適用します。このスキーマは、allOf がスキーマを適用するのと同じ方法で適用されます。何もマージまたは拡張されません。両方のスキーマが独立して適用されます。
たとえば、上記の別の書き方としては、次のようになります。
このインスタンスには credit_card がありますが、billing_address が欠落しています。
こちらには billing_address がありますが、credit_card が欠落しています。ここでは billing_address は追加のプロパティのように見えるため、これはパスします。
dependentRequiredとdependentSchemasは、dependenciesという1つのキーワードでした。依存関係の値が配列の場合、dependentRequiredのように動作し、依存関係の値がスキーマの場合、dependentSchemaのように動作していました。If-Then-Else
if、then、および else キーワードを使用すると、別のスキーマの結果に基づいてサブスキーマを適用できます。これは、従来のプログラミング言語でよく見られる if / then / else 構文によく似ています。
if が有効な場合、then も有効である必要があります(そして else は無視されます)。if が無効な場合、else も有効である必要があります(そして then は無視されます)。
then または else が定義されていない場合、if は値が true であるかのように動作します。
then または else が if なしでスキーマに表示される場合、then と else は無視されます。
これを真理値表の形式で表現すると、if、then、および else が有効な場合の組み合わせと、スキーマ全体の有効性の結果を示します。
| if | then | else | スキーマ全体 |
|---|---|---|---|
| T | T | n/a | T |
| T | F | n/a | F |
| F | n/a | T | T |
| F | n/a | F | F |
| n/a | n/a | n/a | T |
たとえば、アメリカとカナダの住所を処理するスキーマを作成するとします。これらの国では郵便番号の形式が異なり、国に基づいて検証する形式を選択する必要があります。住所がアメリカの場合、postal_code フィールドは「zipcode」で、5桁の数字の後にオプションで4桁のサフィックスが続きます。住所がカナダの場合、postal_code フィールドは、文字と数字が交互に配置された6桁の英数字文字列です。
この例では、"country" は必須プロパティではありません。 if スキーマも "country" プロパティを必須としていないため、これはパスし、"then" スキーマが適用されます。したがって、"country" プロパティが定義されていない場合、デフォルトの動作は "postal_code" を米国の郵便番号として検証することです。"default" キーワードは効果はありませんが、スキーマの読者がデフォルトの動作をより簡単に認識できるように含めることが推奨されます。
残念ながら、上記のアプローチは2つ以上の国にはスケールしません。ただし、if と then のペアを allOf の中にラップして、スケールするものを生成できます。この例では、アメリカとカナダの郵便番号を使用しますが、オランダの郵便番号(4桁の数字と2文字の組み合わせ)も追加します。残りの世界の郵便番号にこれを拡張するのは読者の課題とします。
required キーワードは、if スキーマで必須です。そうしないと、「country」が定義されていない場合に、すべてが適用されてしまいます。「アメリカ合衆国」のif スキーマからrequired を外すと、「country」が定義されていない場合、事実上デフォルトになります。
「country」が必須フィールドであったとしても、各if スキーマにrequired キーワードを含めることをお勧めします。検証結果は同じになります。これは、required が失敗するためですが、それを含めないと、「postal_code」を3つのthen スキーマすべてに対して検証し、無関係なエラーにつながるため、エラー結果にノイズが加わる可能性があります。
含意
Draft 7 より前は、「if-then」条件をスキーマ合成キーワードと「含意」と呼ばれるブール代数の概念を使用して表現できます。 A -> B (A は B を含意すると発音)は、A が真の場合、B も真でなければならないことを意味します。これは、!A || B として表現でき、JSON スキーマとして表現できます。
含意のバリエーションは、if / then / else キーワードで表現できるものと同じものを表現するために使用できます。if / then は A -> B と表現でき、if / else は !A -> B と表現でき、if / then / else は A -> B AND !A -> C と表現できます。
このパターンはあまり直感的ではないため、条件式は記述的な名前を持つ $defs に入れ、"allOf": [{ "$ref": "#/$defs/sit-down-restaurant-implies-tip-is-required" }] を使用してスキーマに $ref で参照することをお勧めします。