JSON Schema 是一套用來描述 JSON 文件結構、約束與型別的詞彙。它讓你能精確定義什麼樣的 JSON 才算合法 —— 哪些鍵必須存在、每個值必須是什麼型別、允許哪些取值 —— 然後依這些規則自動驗證任意 JSON 文件。本指南說明 JSON Schema 是什麼、如何撰寫,以及如何在 JavaScript、Python 與瀏覽器中用它來驗證 JSON。
什麼是 JSON Schema?
JSON Schema 本身就是一份 JSON 文件。它描述另一份 JSON 文件,就像資料庫 schema 描述一張表:它宣告資料預期的形狀、型別與約束。JSON Schema 告訴驗證器 —— 函式庫、API、表單產生器、IDE —— 對某段特定 JSON 而言「合法」意味著什麼。
規格由 json-schema.org 維護,目前是 Draft 2020-12。它被 OpenAPI(描述 REST API 的標準)、JSON Forms(由 schema 生成 UI)、IDE 工具(VS Code 利用 JSON Schema 為 package.json 與 tsconfig.json 提供自動完成)以及無數內部資料管線所使用。
最簡單的 JSON Schema 範例
以下是一份代表使用者的 JSON 文件,以及描述其預期結構的 schema:
// 被驗證的 JSON 文件
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"age": 30,
"active": true
}// JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "name", "email"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"active": { "type": "boolean" }
},
"additionalProperties": false
}這份 schema 表示:文件必須是物件;id、name 與 email 為必填欄位;age 若存在,須介於 0 到 150 之間;且除了列出的五個鍵外不允許其他鍵。
核心關鍵字
type
指定值的 JSON 型別。合法型別包括 "string"、"number"、"integer"、"boolean"、"array"、"object" 與 "null"。也可以用陣列同時允許多種型別:
{ "type": ["string", "null"] } // 值可以是字串或 nullproperties
定義物件中每個鍵的 schema。每一項本身也是一份 schema:
{
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
}
}required
物件中必須出現的鍵名陣列。出現在 properties 中卻不在 required 中的鍵屬於選擇性 —— 可以缺席,但若存在就必須符合其 schema。
items
定義陣列中每個元素的 schema。下面這份 schema 要求陣列元素都是字串:
{
"type": "array",
"items": { "type": "string" }
}字串約束
{
"type": "string",
"minLength": 1,
"maxLength": 100,
"pattern": "^[a-zA-Z0-9_]+quot; // 正規表達式
}數字約束
{
"type": "number",
"minimum": 0,
"maximum": 1,
"multipleOf": 0.01 // 必須是 0.01 的倍數
}enum
將值限定在一組固定的允許取值之中:
{ "enum": ["pending", "active", "suspended", "deleted"] }additionalProperties
控制是否允許出現未在 properties 中列出的鍵。設為 false 可拒絕任何意外的鍵 —— 適用於嚴格的 API 契約:
{ "additionalProperties": false }一個真實世界的 JSON Schema 範例
以下是一份電商 API 中商品的 schema —— 你在 OpenAPI 定義或內部資料契約中會見到的那種:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/product.json",
"title": "Product",
"description": "A product available for purchase",
"type": "object",
"required": ["id", "name", "price", "category"],
"properties": {
"id": {
"type": "integer",
"description": "Unique product identifier"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"price": {
"type": "number",
"minimum": 0,
"description": "Price in USD"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "books", "home", "sports"]
},
"tags": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true
},
"dimensions": {
"type": "object",
"properties": {
"width": { "type": "number", "minimum": 0 },
"height": { "type": "number", "minimum": 0 },
"depth": { "type": "number", "minimum": 0 }
},
"required": ["width", "height", "depth"]
},
"inStock": { "type": "boolean" }
},
"additionalProperties": false
}如何用 schema 驗證 JSON
在挑選 jsonschema 驗證器?標準選擇是 Ajv(JavaScript)、jsonschema(Python),以及 jsonschemavalidator.net 之類的線上服務。如果你需要的是相反方向 —— 由範例產生 json schema,或從一份 json schema 從 json 文件反推 —— 在 Python 可以試 GenSON,在瀏覽器可以試 transform.tools。這些是 json schema generator 工具,從樣本推斷出一份起步用的 schema。事後再手動收緊 required、additionalProperties 與取值約束即可。
在 JavaScript 中(Ajv)
Ajv 是 JavaScript 生態圈中使用最廣的 JSON Schema 驗證器。支援 Draft-07 與 Draft 2020-12。
npm install ajvimport Ajv from 'ajv';
const ajv = new Ajv();
const schema = {
type: 'object',
required: ['id', 'name', 'email'],
properties: {
id: { type: 'integer' },
name: { type: 'string', minLength: 1 },
email: { type: 'string' },
},
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { id: 42, name: 'Alice', email: 'alice@example.com' };
const valid = validate(data);
if (!valid) {
console.error(validate.errors);
} else {
console.log('Valid!');
}ajv.compile(schema) 會回傳一個可重複使用的驗證函式。編譯一次後反覆呼叫,比每次都重新編譯顯著更快。
在 JavaScript 中(瀏覽器、無依賴)
對於只做簡單型別與必填欄位檢查、不想引入相依的情況,可以自己手寫驗證 —— 但只要超 過這種簡單檢查,請使用 Ajv。Schema 驗證邏輯夠複雜,手寫容易出錯。
在 Python 中(jsonschema)
pip install jsonschemaimport json
import jsonschema
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"required": ["id", "name", "email"],
"properties": {
"id": {"type": "integer"},
"name": {"type": "string", "minLength": 1},
"email": {"type": "string"},
},
"additionalProperties": False,
}
data = {"id": 42, "name": "Alice", "email": "alice@example.com"}
try:
validate(instance=data, schema=schema)
print("Valid!")
except ValidationError as e:
print(f"Invalid: {e.message}")以 schema 驗證一份 JSON 檔案
import json
import jsonschema
with open('schema.json') as f:
schema = json.load(f)
with open('data.json') as f:
data = json.load(f)
jsonschema.validate(instance=data, schema=schema)在瀏覽器中(fixjson)
如果你想在開始處理前先確認一份 JSON 文件在結構上是否合法 —— 又不想寫程式 —— fixjson 的 JSON 驗證器 會即時檢查語法並標示錯誤。貼上你的 JSON,任何語法問題都會以行號與精確位置內聯顯示。
若要做完整的 schema 驗證(檢查型別、必填欄位、各種約束),請使用上面任一基於函式庫的方法,或是 jsonschemavalidator.net 之類的專用 schema 測試工具。
$ref:重複使用 schema
實務上 schema 並不會全部內聯定義。$ref 關鍵字讓你能透過 $id 或同一份文件內的 JSON Pointer 路徑參照另一份 schema,讓 schema 保持 DRY 且易於組合。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"address": {
"type": "object",
"required": ["street", "city", "country"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
}
}
},
"type": "object",
"properties": {
"billingAddress": { "$ref": "#/$defs/address" },
"shippingAddress": { "$ref": "#/$defs/address" }
}
}$defs 是在同一份檔案內定義可重用子 schema 的標準位置(在 Draft 2019-09 中取代了 definitions)。
組合 schema:allOf、anyOf、oneOf
JSON Schema 提供三個組合關鍵字,分別表達「必須全部符合」「至少符合其一」「恰好符合其一」:
// allOf: 必須符合列出的每一份 schema
{
"allOf": [
{ "type": "object" },
{ "required": ["id"] },
{ "properties": { "id": { "type": "integer" } } }
]
}
// anyOf: 必須至少符合其中一份 schema
{
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
// oneOf: 必須恰好符合其中一份 schema(各份之間必須互斥)
{
"oneOf": [
{ "properties": { "type": { "const": "circle" } }, "required": ["radius"] },
{ "properties": { "type": { "const": "rectangle" } }, "required": ["width", "height"] }
]
}format 關鍵字:date-time、email、uri、uuid
除了原生型別外,JSON Schema 的 format 關鍵字還可以為字串標註其預期的型態 —— ISO 日期時間、電子郵件、URI、UUID、IP 等等。形式如下:
{
"type": "object",
"properties": {
"id": { "type": "string", "format": "uuid" },
"created_at": { "type": "string", "format": "date-time" },
"email": { "type": "string", "format": "email" },
"homepage": { "type": "string", "format": "uri" }
}
}一個細微但重要的重點:在 Draft 2020-12(以及 2019-09) 中,format 關鍵字預設只是 註解(annotation) —— 除非驗證器被設定成強制執行,否則它不會強制檢查格式。在 Draft-07 與更早版本中,有些驗證器預設將其視為斷言。要在 Ajv 中改成嚴格:
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv); // 註冊 date-time、email、uri、uuid 等
// 在 2020-12 中,你必須於 meta-schema 啟用 format 斷言,或視 Ajv 版本
// 明確傳入 { validateFormats: true }。常見的 format 取值:date-time、date、time、duration、email、idn-email、hostname、ipv4、ipv6、uri、uri-reference、uuid、regex、json-pointer。
把 format 當作提示而不是安全邊界 —— 通過 format: "email" 的字串仍然是不可信輸入。要驗證實際可達或真實有效,請在帶外驗證(DNS、寄出確認信、跑你自己掌控的正規表達式)。
常見的 JSON Schema 誤用
把 "properties" 與 "required" 搞混
把鍵列進 properties 並不代表它就必填。properties 中的鍵只是定義「若它出現」時應遵守的 schema。要強制其存在,還必須把它列入 required。
想表達「number」卻寫了「integer」
"type": "integer" 會拒絕 3.14。如果你的欄位可以是任意數字(包括小數),請改用 "type": "number"。
忘了 "additionalProperties" 預設是 true
預設情況下,JSON Schema 允許任何未列在 properties 中的額外屬性。這是有意為之 —— schema 預設是開放的,遵循 Postel 法則。如果你想要封閉物件,請明確設定 "additionalProperties": false。
不指定 $schema
不同 draft 的語意不同。省略 $schema 關鍵字會讓驗證器去猜該用哪個版本。請務必包含:
{ "$schema": "https://json-schema.org/draft/2020-12/schema" }把 JSON Schema 驗證當作安全驗證
JSON Schema 驗證結構與型別 —— 它不是資料清洗工具。通過 "type": "string" 的字串依然可能包含 SQL 注入、XSS payload 或其他惡意內容。Schema 驗證是第一道閘門,不是完整的安全防護。
JSON Schema 與 OpenAPI
OpenAPI(前身為 Swagger)使用 JSON Schema 的子集來描述請求主體、回應主體與參數。你若寫過 OpenAPI 規格,就等於寫過 JSON Schema —— 雖然 OpenAPI 採用的是帶些擴充與限制的修改方言。
這代表 JSON Schema 知識可以直接遷移到 API 設計。OpenAPI 文件中的 components/schemas 區段就是一組 JSON Schema,而 Stoplight、Redoc 、Swagger UI 等工具會自動將其渲染為互動式文件。
在實務中使用 Ajv
在任何實際專案中,有兩個旗標都值回票價:
strict: true(在現代 Ajv 中為預設) —— 於 schema 編譯時拒絕未知關鍵字與未知 format,在資料被默默放行之前抓出像"minimun"之類的拼字錯誤或漏寫的$schema。allErrors: true—— 收集每一個驗證錯誤,而不是遇到第一個就停止。和ajv.errorsText()配合,適合用於對外的錯誤訊息。
要根據 schema 產生 假 JSON 作為測試與 fixture,可以用 json-schema-faker:它會走訪 schema 並產生符合的值(型別、範圍、列舉,搭配 ajv-formats 時連 format 也能符合)。適合用於 property-based testing 或從 schema 餵 UI 初始資料。
從現有 JSON 產生 JSON Schema
如果你已有 JSON 資料,想從中產生起步用的 schema,有幾個工具可以自動化:
- quicktype.io —— 從範例 JSON 推斷 schema(以及 TypeScript、Go、Python 等的型別定義)
- generate-schema(npm) ——
generate-schema json schema.json data.json - Python:genson ——
pip install genson,然後SchemaBuilder可從一個或多個範例物件推斷出 schema
產生的 schema 只是起點,不是成品。它能從樣本資料推斷型別,卻不認識你的業務規則 —— 它無法推斷某個字串欄位是電子郵件、某個整數必須為正,或是兩個欄位互斥。在正式環境使用前,務必檢查並收緊已產生的 schema。
常見問題
JSON Schema 用來做什麼?
描述並驗證 JSON 資料的結構 —— 哪些鍵必須存在、每個值必須是什麼型別、允許哪些 取值。它支援 OpenAPI、package.json/tsconfig.json 的 IDE 自動完成、表單產生器,以及資料管線的契約。
JSON 和 JSON Schema 有什麼不同?
JSON 是資料;JSON Schema 則是描述「合法資料長什麼樣」的另一份 JSON 文件。如果你還不熟 JSON 格式本身,請先看 什麼是 JSON?。
我該使用哪一版 JSON Schema draft?
Draft 2020-12 是現行版本,新 schema 建議使用;Draft-07 因為工具支援廣泛仍然常見。請務必用 $schema 宣告版本,避免驗證器去猜。
如何依 schema 驗證 JSON?
在 JavaScript 用 Ajv,Python 用 jsonschema 函式庫(上方有範例),或使用網頁版驗證器。要先確認 JSON 本身語法合法,可參考 如何驗證 JSON,或直接交給 驗證器 跑一遍。
JSON Schema 會驗證 format(email、date-time、uri…)嗎?
在 Draft 2020-12 / 2019-09 中,format 關鍵字預設只是註解 —— 驗證器只在被設定時才會強制檢查。在 Ajv 中,註冊 ajv-formats 並啟用 format 斷言,才能真正拒絕格式不符的值。
結語
JSON Schema 把非正式的文件(「這個欄位應該是數字」)變成機器可驗證的契約,各種語言、團隊與系統的驗證器都能一致執行。核心詞彙 —— type、properties、required、items、enum —— 已涵蓋現實大多數使用情境。$ref、組合關鍵字與 format 等較進階的功能則可處理其餘情況。
請從一份只強制必要事項的最小 schema 開始,隨著對資料的理解逐步加入約束。會拒絕合法資料的 schema 比沒有 schema 更糟。
若你要驗證的 JSON 在抵達 schema 驗證器之 前就有語法錯誤,請先修正它們 —— schema 驗證器要求輸入是語法正確的 JSON。JSON 乾淨後,再 驗證它,然後再套用你的 schema。