2020-12 リリースノート
前のドラフト(2019-09)では、$recursiveRef
/$recursiveAnchor
、unevaluatedProperties
/unevaluatedItems
、語彙など、多くの新しい概念が導入されました。それ以来、これらの新機能は、実際のスキーマで複数の実装と使用が行われています。このドラフトは、これらの新機能の実装と実際の使用について学んだ教訓を適用することに関連する変更にほぼ専念しています。
このドキュメントでは、スキーマ作成者にとって最も有用な情報を上部に、実装作成者向けの情報は下部に配置するよう努めています。
itemsとadditionalItemsの変更
配列とタプルを定義するために使用されるキーワードは、JSON Schemaの学習曲線を低くするために再設計されました。items
キーワードが両方の型に使用されていたため、配列を定義するつもりが誤ってタプルを定義してしまい、配列の最初のアイテムのみが検証される理由を理解できないことがよくありました。
items
およびadditionalItems
キーワードは、prefixItems
とitems
に置き換えられました。ここで、prefixItems
は、古いitems
のスキーマの配列と同じ機能を持ち、新しいitems
キーワードは、古いadditionalItems
キーワードと同じ機能を持ちます。
items
の意味は変わりましたが、配列を定義するための構文は変わりません。タプルを定義するための構文のみが変更されました。配列にはアイテム(items
)があり、オプションで、通常のアイテムの前に来る位置的に定義されたアイテム(prefixItems
)があるという考え方です。
変更点を説明するいくつかの例を以下に示します。
オープンスタイルタプル
ドラフト2019-09 | ドラフト2020-12 |
---|---|
データ { "items": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ]} | データ { "prefixItems": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ]} |
クローズドタプル
ドラフト2019-09 | ドラフト2020-12 |
---|---|
データ { "items": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ], "additionalItems": false} | データ { "prefixItems": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ], "items": false} |
制約付きの追加アイテムを持つタプル
ドラフト2019-09 | ドラフト2020-12 |
---|---|
データ { "items": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ], "additionalItems": { "$ref": "#/$defs/baz" }} | データ { "prefixItems": [ { "$ref": "#/$defs/foo" }, { "$ref": "#/$defs/bar" } ], "items": { "$ref": "#/$defs/baz" }} |
$dynamicRef および $dynamicAnchor
$recursiveRef
および $recursiveAnchor
キーワードは、より強力な $dynamicRef
および $dynamicAnchor
キーワードに置き換えられました。$recursiveRef
と $recursiveAnchor
は、再帰的なスキーマを拡張する問題を解決するために以前のドラフトで導入されました。「再帰的」キーワードがいくらか使用され、それらをより良く理解するにつれて、それらを一般化してさらに多くのタイプの問題を解決できることがわかりました。名前の変更は、これらのキーワードが再帰的なスキーマの拡張だけでなく、より多くの用途があることを反映しています。
$dynamicAnchor
は、通常の $anchor
と同様に考えることができますが、定義されたスキーマ内だけでなく、スキーマを横断して参照できる点が異なります。以前の $recursiveAnchor
は、同じように機能すると考えることができますが、スキーマごとに1つしかアンカーを作成できず、スキーマのルートに配置する必要があり、アンカー名は常に空であるという制限がありました。
$dynamicRef
は、以前の $recursiveRef
と同様に機能しますが、フラグメントが空ではなくなり("$dynamicRef": "#my-anchor"
は "$recursiveRef": "#"
の代わりに)、フラグメントのみではないURIも許可されるようになりました。$dynamicRef
にフラグメントのみではないURI参照が含まれている場合、そのURI参照が解決するスキーマが動的解決の開始点として使用されます。
$recursiveRef
を使用しているスキーマを $dynamicRef
を使用するように変換する方法を次に示します。
ドラフト2019-09 | ドラフト2020-12 |
---|---|
|
|
contains および unevaluatedItems
以前のドラフトでは、contains
キーワードが unevaluatedItems
キーワードにどのように影響するか、あるいは影響があるかどうかが指定されていませんでした。このドラフトでは、contains
スキーマの検証に合格した配列内の項目は、「評価済み」と見なされると指定されています。
これにより、以前のドラフトよりも明確に contains
を使用して制約を表現できます。この例では、1つのスキーマに一致する項目と、その他すべてが別のスキーマに一致する配列を表現する方法を示します。
ドラフト2019-09 | ドラフト2020-12 |
---|---|
データ { "type": "array", "contains": { "type": "string" }, "items": { "anyOf": [ { "type": "string" }, { "type": "number" } ] } } | データ { "type": "array", "contains": { "type": "string" }, "unevaluatedItems": { "type": "number" } } |
残念ながら、この変更により、以前は使用できた状況でcontains
が使用できなくなる可能性があります。以下の、2つの文字列のタプルを記述する 2019-09 ドラフトのスキーマを検討してください。このタプルでは、2つのうち1つが3文字以上である必要があり、追加の項目は許可されていません。
このスキーマが与えられた場合、インスタンス ["a", "b", "ccc"]
は、"ccc"
が未評価とみなされ、unevaluatedItems
キーワードに違反するため、失敗します。それでは、その例を単純に draft 2020-12 スキーマに変換してみましょう。
このスキーマが与えられた場合、インスタンス["a", "b", "ccc"]
は、"ccc"
が評価済みとみなされ、unevaluatedItems
キーワードに適用されないため、合格します。この問題を解決するために、contains
キーワードを持つ前に使用していたのと同じブール代数変換を使用できます。
このスキーマが与えられた場合、インスタンス ["a", "b", "ccc"]
は、"ccc"
が評価されていないとみなされ、以前のドラフトと同様に unevaluatedItems
キーワードに失敗するため、失敗します。
正規表現
正規表現は、Unicode文字をサポートすることが期待されるようになりました(ただし、厳密に必須ではありません)。以前は、これは指定されておらず、実装が正規表現でこのUnicodeをサポートしている場合とそうでない場合がありました。
メディアタイプの変更
JSON Schemaは、application/schema+json
と application/schema-instance+json
の2つのメディアタイプを定義しています。このドラフトでは、schema
メディアタイプパラメータのサポートを削除します。これは多くの混乱と不一致を引き起こしていました。実際に使用している証拠が見られないため、当面は削除することにしました。
埋め込みスキーマとバンドル
Draft 2019-09 において、サブスキーマにおける $id
の意味は、現在のスキーマ内でのベース URI の変更を示すものから、親スキーマとは独立した埋め込みスキーマを示すものに変更されました。1つ以上の埋め込みスキーマを含むスキーマは「複合スキーマドキュメント」と呼ばれます。このドラフトでは、複合スキーマドキュメントを作成するために、バンドラーがどのようにスキーマを埋め込むべきかについてのガイダンスを紹介します。
外部スキーマを参照する場合、そのスキーマは独自の $schema
を宣言でき、それは参照元のスキーマの $schema
と異なる可能性があります。実装では、参照先のスキーマの $schema
をサポートしていない場合は、処理モードを切り替えるか、エラーをスローする準備が必要です。埋め込みスキーマもまったく同じように機能します。親スキーマと同じではない $schema
を宣言することができ、実装では $schema
の変更を適切に処理する準備が必要です。
埋め込みスキーマがその親とは異なる $schema
を持つことの注目すべき結果は、実装が複合スキーマドキュメントをメタスキーマに対して直接検証できないことです。複合スキーマドキュメントは分解する必要があり、各スキーマリソースは、そのスキーマの適切なメタスキーマに対して個別に検証する必要があります。
このドラフトでは、スキーマを複合スキーマドキュメントにバンドルするために、埋め込みスキーマをどのように使用するかについての公式ガイダンスを紹介します。このアプローチは、バンドルされたスキーマを検証する場合でも、外部参照をたどる場合でも、出力結果ができるだけ同じになるように、($defs
に追加する以外は)スキーマを変更する必要がないように設計されています。以下は、バンドルしたい外部参照を持つ顧客スキーマの例です。
"type": "object", "properties": { "name": { "type": "string" }, "phone": { "$ref": "/schema/common#/$defs/phone" }, "address": { "$ref": "/schema/address" }
"type": "object", "properties": { "address": { "type": "string" }, "city": { "type": "string" }, "postalCode": { "$ref": "/schema/common#/$defs/usaPostalCode" }, "state": { "$ref": "/$defs/states" } },
"$defs": { "states": { "enum": [...] } }}
"$defs": { "phone": { "type": "string", "pattern": "^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$" }, "usaPostalCode": { "type": "string", "pattern": "^[0-9]{5}(?:-[0-9]{4})?$" }, "unsignedInt": { "type": "integer", "minimum": 0 } }}
これらのスキーマをバンドルするには、$defs
を使用して、参照されている各スキーマを埋め込みスキーマとして追加するだけです。バンドルされたスキーマは次のようになります。
"type": "object", "properties": { "name": { "type": "string" }, "phone": { "$ref": "/schema/common#/$defs/phone" }, "address": { "$ref": "/schema/address" } },
"$defs": { "https://example.com/schema/address": { "$id": "https://example.com/schema/address",
"type": "object", "properties": { "address": { "type": "string" }, "city": { "type": "string" }, "postalCode": { "$ref": "/schema/common#/$defs/usaPostalCode" }, "state": { "$ref": "#/$defs/states" } },
"$defs": { "states": { "enum": [...] } } }, "https://example.com/schema/common": { "$schema": "https://json-schema.dokyumento.jp/draft/2019-09", "$id": "https://example.com/schema/common",
"$defs": { "phone": { "type": "string", "pattern": "^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$" }, "usaPostalCode": { "type": "string", "pattern": "^[0-9]{5}(?:-[0-9]{4})?$" }, "unsignedInt": { "type": "integer", "minimum": 0 } } } }}
この例から気づくかもしれない点がいくつかあります。
$ref
は変更されていません。ローカル参照も変更されていません。https://example.com/schema/common#/
$defs/unsignedInt
は、使用されていなくても、共通スキーマと一緒にプルインされました。余分な定義を削除することは許可されていますが、必須ではありません。https://example.com/schema/address
は$schema
を宣言していません。https://example.com/schema/customer
と同じ$schema
を使用しているため、その宣言をスキップし、埋め込まれているスキーマの$schema
を使用できます。https://example.com/schema/common
は、埋め込まれているドキュメントとは異なる$schema
を使用しています。これは許可されています。https://example.com/schema/common
の定義は、他の両方のスキーマで使用されており、一度だけ含める必要があります。バンドラーが埋め込みスキーマの中にスキーマを埋め込む必要はありません。
注釈
アノテーションを収集する実装は、"verbose" 出力形式で、不明なキーワードのアノテーションを含める必要があります。不明なキーワードのアノテーション値は、そのキーワードの値です。
語彙の変更
unevaluatedProperties
および unevaluatedItems
キーワードは、アプリケーター語彙から、デフォルトのメタスキーマで必須となる独自の指定された語彙に移動しました。Draft 2019-09 では、これらのキーワードが実装されていない場合、エラーをスローすることが期待されていました。これは、アプリケーター語彙の特殊なケースの動作でした。「未評価」キーワードを独自の語彙に移動することで、この特殊なケースを削除し、これらのキーワードを必要としない方言を構築できるようになります。
フォーマット語彙は、2つの独立した語彙に分割されました。「format-annotation」語彙は、format
キーワードをアノテーションとして扱い、「format-assertion」語彙は、format
キーワードをアサーションとして扱います。「format-annotation」語彙は、デフォルトのメタスキーマで使用され、必須です。Draft 2019-09 では、format
はデフォルトでアノテーションとして評価されるべきであり、実装は format
をアサーションとして評価するように動作を変更するための設定を提供することができました。個別の語彙により、特別な設定要件を削除し、どの動作を使用するかを語彙システムを使用して表現できるようになります。