1. 為何需要 JSON 驗證
想像一下:你寫了一個 User 類別,並預期輸入永遠會是下面這樣的 JSON:
{
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
但結果卻收到這樣的內容:
{
"id": "四十二",
"name": 123,
"email": null,
"admin": true
}
甚至是這樣:
{
"username": "Alice"
}
在最好情況下,Jackson 或 Gson 會在嘗試反序列化時丟出例外;更糟的是——它們可能默默將欄位設為預設值,導致你的業務程式碼行為不正確。而如果這是服務的設定檔——就可能踩到讓整個團隊一路抓蟲的麻煩問題。
JSON 驗證 是檢查 JSON 中結構、型別與數值是否符合既定規則(Schema)的過程。這就像資料的護照查驗:沒通過——就不讓上機!
2. JSON Schema:是什麼、長什麼樣
在 JSON 的世界裡,有一個用來描述資料結構的官方標準——JSON Schema。它就像一份「檢查清單」,用來核對 JSON 是否符合你的程式需求。
JSON Schema 本身也是 JSON,只是包含一些特殊鍵:type、properties、required 等等。
最簡單的 Schema 範例
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
這裡在做什麼:
- 期望是一個物件(type: "object")。
- 該物件可以有欄位 "id"、"name"、"email"(在 properties 中描述)。
- "id"——必須是整數(type: "integer")。
- "name"——必須是字串(type: "string")。
- "email"——是看起來像 email 的字串(鍵 format,值 "email")。
- required 指出必要欄位清單:"id" 與 "name"。
如果 JSON 缺少 "id" 或 "name",或其型別不正確——驗證將不通過。
JSON Schema 的能力概覽
- 指定型別(type: "string"、"integer"、"array"、"object"、"boolean"、"null")。
- 描述巢狀物件與陣列(properties、items)。
- 必要與非必要欄位(required)。
- 檢查字串長度、數值範圍(minLength、maximum 等)。
- 格式檢查(format: "email"、"date"、"uri" 等)。
- 列舉(enum:允許值清單)。
- 字串的正規表示式(pattern)。
- 複雜條件:anyOf、oneOf、allOf(進階情境)。
3. 在 Java 中驗證 JSON:函式庫概覽
Java 標準程式庫不包含基於 Schema 的 JSON 驗證,但有一些熱門的第三方函式庫。以下是最常見的:
- everit-org/json-schema——簡單、免費且受歡迎。
- networknt/json-schema-validator——速度快,支援最新標準。
- Jackson-module-jsonSchema——Jackson 的延伸模組(但不支援完整驗證)。
- Justify、Java JSON Tools——還有其他,不過較少見。
本講座我們選用 everit-org/json-schema——對新手友善、文件齊全,且不需要繞圈子的繁瑣操作。
安裝 everit-org/json-schema
在你的 pom.xml(Maven)加入相依:
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.14.2</version>
</dependency>
或者透過 Gradle:
implementation 'org.everit.json:org.everit.json.schema:1.14.2'
4. 範例:依照 Schema 驗證 JSON(逐步)
讓我們實作一次驗證。需要一份 Schema、JSON,以及一點程式碼。
Schema 範例(user-schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
有效的 JSON 範例(user.json):
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
無效的 JSON 範例:
{
"id": "一",
"name": "",
"email": "not-an-email"
}
驗證程式碼
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class JsonValidationExample {
public static void main(String[] args) throws Exception {
// 從檔案載入 Schema
String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
Schema schema = SchemaLoader.load(rawSchema);
// 載入待驗證的 JSON
String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
JSONObject json = new JSONObject(new JSONTokener(jsonString));
// 驗證
try {
schema.validate(json); // 若一切正常——不會有任何動作
System.out.println("JSON 有效!");
} catch (ValidationException e) {
System.out.println("JSON 無效!");
for (String msg : e.getAllMessages()) {
System.out.println("錯誤: " + msg);
}
}
}
}
程式碼說明:
- 使用 Schema 類別與載入器 SchemaLoader.load(...)。
- schema.validate(json) 在與 Schema 不符時會拋出例外。
- 在 catch 區塊可透過 getAllMessages() 取得所有錯誤。
如何整合到應用程式?
通常會把 Schema 放在資源目錄(例如 resources 資料夾)。在將 JSON 反序列化為 Java 物件之前先進行驗證。若一切正常,就反序列化並繼續處理。
5. 處理驗證錯誤
當 JSON 未通過檢查時,函式庫會拋出 ValidationException。訊息中包含錯誤清單:哪些地方不符合 Schema。
錯誤輸出的範例
對於上面的無效 JSON,輸出大致如下:
JSON 無效!
錯誤: #: required key [id] not found
錯誤: #/name: expected minLength: 2, actual: 0
錯誤: #/email: String [not-an-email] is invalid against requested format [email]
錯誤: #/id: expected type: Integer, found: String
如何解讀這些錯誤:
- required key [id] not found——缺少必要欄位。
- expected minLength: 2, actual: 0——字串太短。
- String [...] is invalid against requested format [email]——email 格式不正確。
- expected type: Integer, found: String——型別不相符。
重要! 訊息可能是英文,但通常相當易懂。
如何把錯誤顯示給使用者?
你可以把錯誤收集成清單並回傳給使用者,例如透過 REST API 或 GUI。這能讓他們快速了解輸入資料哪裡有問題。
6. 實作:驗證物件陣列
經常需要驗證的不只是單一物件,而是一個陣列:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "這是什麼?", "name": 123, "email": "not-an-email" }
]
Schema:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
驗證 的方式相同,只是這次傳入的是陣列。錯誤訊息會包含元素索引,指出哪個位置有問題(例如 [#/2/id])。
7. JSON 驗證的常見錯誤
錯誤 1:資料型別不符。 很常見的是以字串代替數字("id": "123"),但 Schema 期望的是 integer。驗證將不通過。若你無法控制資料來源——不是調整 Schema,就是事先轉換資料。
錯誤 2:缺少必要欄位。 如果在 Schema 中標示了必要欄位("required": ["id","name"]),而 JSON 中沒有——就會產生錯誤。有時這會出乎意料:是前端忘了傳某個欄位,或是 API 已改版。
錯誤 3:JSON 中出現多餘欄位。 預設情況下,JSON Schema 允許多餘欄位。若你想要嚴格的 Schema,別忘了加上 "additionalProperties": false。否則 JSON 可以包含任何「不相干」的欄位。
錯誤 4:Schema 版本或語法不正確。 如果使用了當前 Schema 版本不存在的鍵,或出現拼寫錯誤,驗證器將無法載入 Schema。請在 https://www.jsonschemavalidator.net/ 或類似服務上檢查你的 Schema。
錯誤 5:錯誤處理不佳。 若只捕捉第一個例外,卻不向使用者呈現細節,將很難知道 JSON 到底哪裡出了問題。請使用 getAllMessages() 輸出所有錯誤。
錯誤 6:過度倚賴 format 檢查。 "format": "email" 或 "date" 的檢查常常比較「粗略」。若你需要更嚴格的驗證,請在程式中補充額外的檢查。
GO TO FULL VERSION