JSON Schema の draft 差分:04 / 06 / 07 / 2019-09 / 2020-12 を移行する前に知っておくこと
JSON Schema には draft-04 から最新の 2020-12 まで複数のリビジョンがあり、それぞれが微妙に互換性のない仕様変更を含んでいます。「ライブラリ A で書いた schema がライブラリ B で動かない」「OpenAPI を 3.0 から 3.1 に上げたら schema 検証が壊れた」は典型的なドラフト不整合です。本記事では各 draft の主な変更点と、移行で踏みやすい差分を整理します。
ドラフト一覧
| Draft | 公開時期 | $schema URL の # |
|---|---|---|
| draft-04 | 2013 | http://json-schema.org/draft-04/schema# |
| draft-06 | 2017 | http://json-schema.org/draft-06/schema# |
| draft-07 | 2018 | http://json-schema.org/draft-07/schema# |
| draft 2019-09 | 2019 | https://json-schema.org/draft/2019-09/schema |
| draft 2020-12 | 2020 | https://json-schema.org/draft/2020-12/schema |
draft-05 はスキップされ、2019 以降は YYYY-MM 形式に移行。
主要な変更点
id → $id(draft-06)
draft-04 では schema 識別子が id キーで、$ref の解決基準にも使われていました。他のキーワードと混在しやすいため、draft-06 で $id に改名されました。
// draft-04
{ "id": "http://example.com/schema.json" }
// draft-06+
{ "$id": "http://example.com/schema.json" } exclusiveMinimum / exclusiveMaximum(draft-06)
draft-04 では真偽値の補助:
// draft-04
{ "minimum": 0, "exclusiveMinimum": true } draft-06 で数値そのものを受け取る形式に:
// draft-06+
{ "exclusiveMinimum": 0 } draft-04 のスキーマを draft-06+ のバリデータに渡すと、exclusiveMinimum: true が「0 より大」ではなく「true より大」と解釈される可能性があります(多くのライブラリは互換モードを持つが、デフォルトでは厳密に新仕様で解釈)。
const・contains・propertyNames(draft-06)
新キーワードが追加:
{
"const": "fixed-value", // draft-06: 単一値
"contains": { "type": "string" }, // draft-06: 配列の少なくとも 1 要素にマッチ
"propertyNames": { "pattern": "^[a-z]+$" } // draft-06: object のキー名を制約
} if / then / else(draft-07)
条件分岐:
{
"if": { "properties": { "country": { "const": "US" } } },
"then": { "required": ["postalCode"] },
"else": { "required": ["zip"] }
} draft-06 まではこの種の条件は oneOf + 否定で表現する必要があり、複雑でした。
$ref と他キーの共存(2019-09)
draft-07 までは $ref を含むオブジェクトの他のキーは無視されるルールでした:
// draft-07: description は無視される
{
"$ref": "#/definitions/User",
"description": "ユーザー情報"
} 2019-09 で他キーも有効になるように変更。description や examples を $ref と並べて書けるようになりました。
unevaluatedProperties / unevaluatedItems(2019-09)
additionalProperties は自身の properties で定義されていないキーを制御しますが、allOf で他のスキーマから来る properties は無視されます。unevaluatedProperties はスキーマ全体(allOf 等の合成も含む)から見て未評価のプロパティを制御する新キーワード。
{
"allOf": [{ "properties": { "name": { "type": "string" } } }],
"unevaluatedProperties": false // name 以外を全て拒否
} OpenAPI 3.1 や複雑なスキーマ合成で頻出。
$dynamicRef / $dynamicAnchor(2020-12)
$ref の動的版で、$ref の解決時に「呼び出し元のスキーマで定義された anchor を優先する」挙動。再帰的・拡張可能なスキーマ(自分自身を参照しつつ、子スキーマ側でフックを差し替えたい場合)で使います。
実装が複雑なため、サポートしているライブラリは主要なものに限られます(Ajv、JSV、json-everything 等)。
dependentRequired / dependentSchemas(2019-09)
draft-07 までの dependencies キーワードを 2 つに分割:
// draft-07: 1 キーで両用
{ "dependencies": { "creditCard": ["billingAddress"] } }
// 2019-09+
{ "dependentRequired": { "creditCard": ["billingAddress"] } } 意味は同じだが命名が分かれた。
互換性マトリクス(実装側)
代表的なバリデータの draft 対応:
| ライブラリ | draft-04 | draft-06 | draft-07 | 2019-09 | 2020-12 |
|---|---|---|---|---|---|
| Ajv (Node.js) | ✓(旧版) | ✓ | ✓ | ✓ (Ajv 8) | ✓ (Ajv 8) |
| jsonschema (Python) | ✓ | ✓ | ✓ | ✓ | ✓ |
| everit-json-schema (Java) | ✓ | ✓ | ✓ | △(一部) | ✗ |
| json-everything (.NET) | ✓ | ✓ | ✓ | ✓ | ✓ |
| gojsonschema (Go) | ✓ | ✓ | ✓ | ✗ | ✗ |
| santhosh-tekuri/jsonschema (Go) | ✓ | ✓ | ✓ | ✓ | ✓ |
Go の標準的なバリデータが 2019-09+ をサポートしていないことに注意。OpenAPI 3.1 を Go で扱うときの躓きポイント。
OpenAPI との関係
OpenAPI のバージョンと内部 JSON Schema の関係:
- OpenAPI 3.0 → JSON Schema draft-05 ベース(実態はそれを微改造)
- OpenAPI 3.1 → JSON Schema draft 2020-12 と完全互換
OpenAPI 3.0 の schema には nullable: true という独自フィールドがあり、これは JSON Schema 標準ではありません。OpenAPI 3.1 では type: ["string", "null"] のような正規の構文を使います。3.0 → 3.1 移行時に この置換が要ります。
移行時のチェックリスト
draft を上げるときに踏みやすい差分:
id→$idに書き換えるexclusiveMinimum: trueが残っていないか確認、exclusiveMinimum: <数値>に書き換えdependenciesをdependentRequired/dependentSchemasに分離$refと他キーの共存:description などが効くようになる(意図しない場合は別オブジェクトに分離)unevaluatedProperties: falseを追加すべき箇所がないか(spread / allOf 合成があるとき)nullable: true(OpenAPI 3.0 専用) →type: [..., "null"]に変換- バリデータライブラリが対象 draft をサポートしているか確認
実用上の指針
- 新規プロジェクト → 2020-12 が第一候補(最新で、OpenAPI 3.1 と一致)
- 既存システム互換 → 既存 schema の
$schemaに合わせ、その draft でロックイン - マルチ言語環境(Go、Java を含む)でバリデーションを共有 → draft-07 が最も互換性が高い(最大公約数)
- OpenAPI 3.0 を維持 → schema は draft-05 もどき。3.1 に移行できるなら 2020-12 へ統一
まとめ
JSON Schema の draft 差分は「微妙に違う」ものが多く、とくに $id・exclusiveMinimum・$ref の挙動・unevaluatedProperties あたりは互換性を壊します。$schema の URL を必ず明記し、利用側のバリデータがその draft をサポートしていることを確認するのが最大公約数の安全策です。
JSON Schema を実際に書き、検証する作業は JSON バリデータツール で確認できます。