リファレンス
サブスキーマを条件付きで適用する
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
で参照することをお勧めします。