CodeGym /課程 /JAVA 25 SELF /JSON 驗證:JSON Schema,驗證錯誤

JSON 驗證:JSON Schema,驗證錯誤

JAVA 25 SELF
等級 46 , 課堂 4
開放

1. 為何需要 JSON 驗證

想像一下:你寫了一個 User 類別,並預期輸入永遠會是下面這樣的 JSON:

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com"
}

但結果卻收到這樣的內容:

{
  "id": "四十二",
  "name": 123,
  "email": null,
  "admin": true
}

甚至是這樣:

{
  "username": "Alice"
}

在最好情況下,JacksonGson 會在嘗試反序列化時丟出例外;更糟的是——它們可能默默將欄位設為預設值,導致你的業務程式碼行為不正確。而如果這是服務的設定檔——就可能踩到讓整個團隊一路抓蟲的麻煩問題。

JSON 驗證 是檢查 JSON 中結構、型別與數值是否符合既定規則(Schema)的過程。這就像資料的護照查驗:沒通過——就不讓上機!

2. JSON Schema:是什麼、長什麼樣

在 JSON 的世界裡,有一個用來描述資料結構的官方標準——JSON Schema。它就像一份「檢查清單」,用來核對 JSON 是否符合你的程式需求。

JSON Schema 本身也是 JSON,只是包含一些特殊鍵:typepropertiesrequired 等等。

最簡單的 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")。
  • 描述巢狀物件與陣列(propertiesitems)。
  • 必要與非必要欄位(required)。
  • 檢查字串長度、數值範圍(minLengthmaximum 等)。
  • 格式檢查(format: "email""date""uri" 等)。
  • 列舉(enum:允許值清單)。
  • 字串的正規表示式(pattern)。
  • 複雜條件:anyOfoneOfallOf(進階情境)。

3. 在 Java 中驗證 JSON:函式庫概覽

Java 標準程式庫不包含基於 Schema 的 JSON 驗證,但有一些熱門的第三方函式庫。以下是最常見的:

  • everit-org/json-schema——簡單、免費且受歡迎。
  • networknt/json-schema-validator——速度快,支援最新標準。
  • Jackson-module-jsonSchema——Jackson 的延伸模組(但不支援完整驗證)。
  • JustifyJava JSON Tools——還有其他,不過較少見。

本講座我們選用 everit-org/json-schema——對新手友善、文件齊全,且不需要繞圈子的繁瑣操作。

安裝 everit-org/json-schema

在你的 pom.xmlMaven)加入相依:

<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" 的檢查常常比較「粗略」。若你需要更嚴格的驗證,請在程式中補充額外的檢查。

1
問卷/小測驗
JSON 序列化,等級 46,課堂 4
未開放
JSON 序列化
JSON 序列化
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION