draft-07 和 2020-12 該用哪個
結論:2020-12 是當前推薦版本,`$defs`、`prefixItems`、`unevaluatedProperties` 都在這裡;但很多舊驗證器、程式碼產生器仍只認 draft-07。選版本的依據不是新舊,而是「鏈路上每個工具都支援哪個」。

結論:新專案直接用 2020-12,只要驗證器、程式碼產生器或 OpenAPI 工具鏈中有元件只認 draft-07,就退回 draft-07,並在檔案裡寫死 $schema。
一句話:新專案用 2020-12,鏈路上有老元件就退回 draft-07。
版本演進速覽
| 版本 | 年份 | 關鍵變化 |
|---|---|---|
| draft-04 | 2013 | 早期廣泛使用,id 而非 $id |
| draft-06 | 2017 | 加 const、propertyNames |
| draft-07 | 2018 | 加 if/then/else、readOnly、contentMediaType,相容性最好 |
| 2019-09 | 2019 | $defs 取代 definitions,加 unevaluatedProperties |
| 2020-12 | 2020 | prefixItems 取代陣列元組寫法,$dynamicRef,當前推薦 |
2020-12 好用的地方
陣列元組寫法統一了。 draft-07 裡用 items: [{...}, {...}] 表示「第一個元素是 A、第二個是 B」,additionalItems 控制後續;2020-12 改成 prefixItems + items,語意清楚得多。
$defs 更規範。 2019-09 之後官方統一為 $defs,新工具預設按這個找。
unevaluatedProperties 解決組合難題。 用 allOf 組合多個 Schema 時,additionalProperties: false 會誤殺其他分支定義的欄位;unevaluatedProperties: false 只看「最終求值過的屬性」,這才是組合場景想要的行為。
什麼時候必須退回 draft-07
| 情況 | 原因 |
|---|---|
| 使用 OpenAPI 3.0 | 規範內建的就是 draft-04/07 的子集 |
| 驗證器是老版本(ajv 6、部分 Java 函式庫) | 不認識 prefixItems、$dynamicRef |
| 平台方指定 Schema 版本 | 例如某些低程式碼平台、API 閘道 |
| 產生程式碼的工具只支援到 07 | 產生器會靜默忽略不認識的關鍵字 |
判斷方法很直接:拿一段用 prefixItems 的 Schema 丟進你的驗證器,如果它不報「未知關鍵字」而是直接通過驗證,說明它在靜默忽略——這比報錯更危險。
混用時的注意事項
永遠寫 $schema。 不寫時不同函式庫的預設版本不一樣,ajv 預設 draft-07,部分 Python 函式庫預設 2020-12。同一個檔案在兩處驗證結果不同,排查起來非常費勁。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/order.json",
"type": "object"
}
別跨版本引用。 $ref 指向另一個版本的 Schema,行為未定義。
升級要成批做。 一個專案裡同時存在兩種版本,等於把「這份檔案到底按哪個版本解釋」變成人員記憶。
常見問題
如何判斷驗證器是否真的支援 2020-12?
拿一段使用 prefixItems 的 Schema 丟進驗證器:報「未知關鍵字」說明不支援,直接通過驗證則說明它在靜默忽略,這比報錯更危險。
為什麼要讓整個專案統一版本?
一個專案同時存在兩種版本,等於把「這份檔案按哪個版本解釋」變成人員記憶,早晚出錯。升級應當成批完成。
是否可以不寫 $schema?
不建議。不寫時不同函式庫的預設版本不一樣,ajv 預設 draft-07,部分 Python 函式庫預設 2020-12,同一個檔案在兩處驗證結果可能不同。