CodeGym /Các khóa học /JAVA 25 SELF /Xác thực JSON: JSON Schema, lỗi xác thực

Xác thực JSON: JSON Schema, lỗi xác thực

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Tại sao cần xác thực JSON

Hãy hình dung: bạn đã viết lớp User và mong đợi đầu vào luôn là JSON như sau:

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

Nhưng bất ngờ bạn nhận được như thế này:

{
  "id": "bốn mươi hai",
  "name": 123,
  "email": null,
  "admin": true
}

Hoặc thậm chí:

{
  "username": "Alice"
}

Trong trường hợp tốt nhất, Jackson hoặc Gson sẽ ném ngoại lệ khi cố gắng giải tuần tự. Tệ hơn — âm thầm gán giá trị mặc định cho các trường, và mã nghiệp vụ của bạn sẽ hoạt động không chính xác. Còn nếu đây là cấu hình cho dịch vụ của bạn — có thể bạn sẽ gặp những lỗi “khó đỡ” mà sau đó cả đội phải đi tìm.

Xác thực JSON là quá trình kiểm tra rằng cấu trúc, kiểu và giá trị dữ liệu trong JSON tuân theo các quy tắc được xác định (schema). Nó giống như kiểm soát hộ chiếu cho dữ liệu: không vượt qua — không được lên tàu!

2. JSON Schema: là gì và trông như thế nào

Trong thế giới JSON có một tiêu chuẩn chính thức để mô tả cấu trúc dữ liệu — JSON Schema. Đây như một “danh sách kiểm” để kiểm tra xem JSON có phù hợp với yêu cầu của chương trình bạn hay không.

JSON Schema cũng là JSON, chỉ khác là có các khóa đặc biệt: type, properties, required và tương tự.

Ví dụ về schema đơn giản nhất

{
  "$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"]
}

Ở đây có gì:

  • Kỳ vọng một đối tượng (type: "object").
  • Đối tượng có thể có các trường "id", "name", "email" (mô tả trong properties).
  • "id" — bắt buộc là số nguyên (type: "integer").
  • "name" — bắt buộc là chuỗi (type: "string").
  • "email" — chuỗi có định dạng giống email (khóa format với giá trị "email").
  • required chỉ ra danh sách trường bắt buộc: "id""name".

Nếu trong JSON thiếu "id" hoặc "name", hoặc kiểu của chúng không đúng — xác thực sẽ không qua.

Tóm tắt khả năng của JSON Schema

  • Chỉ định kiểu (type: "string", "integer", "array", "object", "boolean", "null").
  • Mô tả đối tượng và mảng lồng nhau (properties, items).
  • Các trường bắt buộc và không bắt buộc (required).
  • Kiểm tra độ dài chuỗi, khoảng giá trị số (minLength, maximum, v.v.).
  • Kiểm tra định dạng (format: "email", "date", "uri", v.v.).
  • Liệt kê (enum: danh sách giá trị hợp lệ).
  • Biểu thức chính quy cho chuỗi (pattern).
  • Điều kiện phức tạp: anyOf, oneOf, allOf (cho các trường hợp nâng cao).

3. Xác thực JSON trong Java: tổng quan thư viện

Thư viện chuẩn của Java không bao gồm xác thực JSON theo schema. Nhưng có nhiều thư viện bên thứ ba phổ biến. Đây là những cái nổi tiếng:

  • everit-org/json-schema — đơn giản, miễn phí và phổ biến.
  • networknt/json-schema-validator — nhanh, hỗ trợ các tiêu chuẩn mới nhất.
  • Jackson-module-jsonSchema — phần mở rộng cho Jackson (nhưng không hỗ trợ xác thực đầy đủ).
  • Justify, Java JSON Tools — còn những cái khác, nhưng ít gặp hơn.

Trong bài giảng này, chúng ta sẽ xem everit-org/json-schema — dễ cho người mới, tài liệu tốt và không cần “múa may” phức tạp.

Cài đặt everit-org/json-schema

Thêm phụ thuộc vào pom.xml của bạn (Maven):

<dependency>
  <groupId>org.everit.json</groupId>
  <artifactId>org.everit.json.schema</artifactId>
  <version>1.14.2</version>
</dependency>

Hoặc qua Gradle:

implementation 'org.everit.json:org.everit.json.schema:1.14.2'

4. Ví dụ: xác thực JSON theo schema (từng bước)

Hãy thử xác thực JSON trong thực tế. Chúng ta cần schema, JSON và một chút mã.

Ví dụ 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"]
}

Ví dụ JSON hợp lệ (user.json):

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

Ví dụ JSON không hợp lệ:

{
  "id": "một",
  "name": "",
  "email": "not-an-email"
}

Mã để xác thực

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 {
        // Tải schema từ tệp
        String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
        JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
        Schema schema = SchemaLoader.load(rawSchema);

        // Tải JSON để kiểm tra
        String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
        JSONObject json = new JSONObject(new JSONTokener(jsonString));

        // Xác thực
        try {
            schema.validate(json); // Nếu mọi thứ ổn — sẽ không có gì xảy ra
            System.out.println("JSON hợp lệ!");
        } catch (ValidationException e) {
            System.out.println("JSON KHÔNG hợp lệ!");
            for (String msg : e.getAllMessages()) {
                System.out.println("Lỗi: " + msg);
            }
        }
    }
}

Ghi chú cho mã:

  • Sử dụng lớp Schema và bộ nạp SchemaLoader.load(...).
  • Phương thức schema.validate(json) ném ngoại lệ nếu không phù hợp với schema.
  • Trong khối catch có thể lấy tất cả lỗi qua getAllMessages().

Tích hợp vào ứng dụng như thế nào?

Thông thường schema được lưu trong tài nguyên (ví dụ, thư mục resources). Bạn xác thực JSON trước khi giải tuần tự sang đối tượng Java. Nếu mọi thứ ổn — giải tuần tự và tiếp tục xử lý.

5. Xử lý lỗi xác thực

Khi JSON không vượt qua kiểm tra, thư viện sẽ ném ngoại lệ ValidationException. Thông báo chứa danh sách lỗi: điều gì không khớp với schema.

Ví dụ kết quả lỗi

Với JSON không hợp lệ ở trên, đầu ra sẽ xấp xỉ như sau:

JSON KHÔNG hợp lệ!
Lỗi: #: required key [id] not found
Lỗi: #/name: expected minLength: 2, actual: 0
Lỗi: #/email: String [not-an-email] is invalid against requested format [email]
Lỗi: #/id: expected type: Integer, found: String

Cách diễn giải lỗi:

  • required key [id] not found — thiếu trường bắt buộc.
  • expected minLength: 2, actual: 0 — chuỗi quá ngắn.
  • String [...] is invalid against requested format [email] — email không hợp lệ.
  • expected type: Integer, found: String — kiểu không khớp.

Quan trọng! Thông báo có thể bằng tiếng Anh, nhưng khá dễ hiểu.

Hiển thị lỗi cho người dùng như thế nào?

Bạn có thể gom lỗi thành danh sách và trả về cho người dùng, ví dụ, trong REST API hoặc GUI. Nhờ vậy có thể nhanh chóng hiểu dữ liệu đầu vào sai ở đâu.

6. Thực hành: xác thực mảng các đối tượng

Thường chúng ta cần xác thực không chỉ một đối tượng, mà là một mảng:

[
  { "id": 1, "name": "Alice", "email": "alice@example.com" },
  { "id": 2, "name": "Bob" },
  { "id": "đây là gì?", "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"]
  }
}

Xác thực hoạt động tương tự, chỉ khác là ta truyền vào một mảng JSON. Lỗi sẽ chứa chỉ số phần tử nơi có vấn đề (ví dụ, [#/2/id]).

7. Các lỗi thường gặp khi xác thực JSON

Lỗi số 1: Không khớp kiểu dữ liệu. Rất thường gặp chuỗi thay vì số ("id": "123"), trong khi schema yêu cầu integer. Sẽ không vượt qua xác thực. Nếu bạn không kiểm soát nguồn dữ liệu — hoặc sửa schema, hoặc chuyển đổi dữ liệu trước.

Lỗi số 2: Thiếu trường bắt buộc. Nếu trong schema chỉ ra trường là bắt buộc ("required": ["id","name"]), nhưng trong JSON không có — bạn sẽ nhận lỗi. Đôi khi điều này bất ngờ: frontend quên gửi trường hoặc API đã thay đổi.

Lỗi số 3: Trường thừa trong JSON. Theo mặc định, JSON Schema cho phép có trường thừa. Nếu bạn muốn schema nghiêm ngặt, đừng quên thêm "additionalProperties": false. Nếu không, JSON có thể chứa bất kỳ trường “lạ” nào.

Lỗi số 4: Phiên bản schema hoặc cú pháp không đúng. Nếu bạn dùng các khóa không có trong phiên bản schema của bạn, hoặc gõ sai, validator sẽ không thể tải schema. Hãy kiểm tra schema tại https://www.jsonschemavalidator.net/ hoặc các dịch vụ tương tự.

Lỗi số 5: Xử lý lỗi kém. Nếu chỉ bắt ngoại lệ đầu tiên và không hiển thị chi tiết cho người dùng, sẽ khó hiểu JSON sai ở đâu. Hãy dùng getAllMessages() để in ra tất cả lỗi.

Lỗi số 6: Các format quá nghiêm ngặt. Kiểm tra "format": "email" hoặc "date" đôi khi khá “sơ sài”. Nếu bạn cần xác thực chặt chẽ, hãy bổ sung kiểm tra thêm trong mã.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Tuần tự hóa JSON
Tuần tự hóa JSON
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION