1. Giới thiệu
Vậy là bạn đã biết chuyển đối tượng thành JSON (và ngược lại). Nhưng làm gì nếu vào chương trình của bạn đến một thứ… lạ lùng? Ví dụ, bạn mong đợi JSON như sau:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
Nhưng nhận được:
{
"name": 42,
"id": "not a number"
}
Ừ, các serializer của C# sẽ cố gắng deserialize cái hỗn loạn đó, nhưng thường điều này dẫn tới lỗi runtime, mất dữ liệu hoặc thậm chí phá vỡ business. Trước khi gửi dữ liệu đi tiếp, lưu vào DB hoặc gửi qua mạng, hãy kiểm tra rằng dữ liệu hợp lệ — nếu không code của bạn giống như diễn viên nhào lộn không có dây an toàn.
Đó là lý do cần validation: nó tự động kiểm tra rằng JSON tuân theo quy tắc — kiểu giá trị, tính bắt buộc của trường, phạm vi, cấu trúc, v.v.
2. Có những cách nào để validate JSON?
Trong C# và .NET có ba cách tiếp cận chính:
- Validation bằng code tự viết: parse object thủ công, viết các kiểm tra và ném exception khi có lỗi.
- Attributes validation trên model: ví dụ [Required], [Range], [EmailAddress] từ namespace System.ComponentModel.DataAnnotations.
- Validation bằng JSON Schema — nhân vật chính hôm nay!
JSON Schema là một chuẩn cho phép mô tả chính thức xem một document hợp lệ trông thế nào. Schema cũng là JSON. Bạn chỉ định trường nào cần, kiểu gì, giá trị nào chấp nhận được, v.v.
Bằng schema bạn mô tả:
- Trường nào phải tồn tại.
- Kiểu dữ liệu mong đợi (string, array, number, object…).
- Trường nào bắt buộc, trường nào không.
- Phạm vi giá trị (ví dụ tuổi từ 0 đến 150).
- Mask, độ dài, danh sách giá trị được phép, v.v.
Ví dụ schema đơn giản nhất
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. Các thành phần chính của JSON Schema
Ta cùng xem ví dụ để hiểu các khóa chính trong JSON Schema nghĩa là gì.
| Khóa | Mô tả | Ví dụ |
|---|---|---|
|
Link tới phiên bản chuẩn của JSON Schema | |
|
Kiểu giá trị (object, array, string, number, v.v.) | |
|
Mô tả các thuộc tính của object | |
|
Mảng các trường bắt buộc | |
|
Mô tả kiểu phần tử của mảng | |
|
Danh sách các giá trị cho phép | |
, |
Ràng buộc cho số | |
, |
Giới hạn độ dài chuỗi | |
|
Regular expression cho chuỗi | |
Minh họa: ví dụ schema cho mảng người
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
4. Cách validate JSON theo schema trong C#
Trong .NET không có hỗ trợ chuẩn cho JSON Schema (tính tới .NET 9). Thực tế thường dùng thư viện bên ngoài: NJsonSchema hoặc Newtonsoft.Json.Schema (Json.NET Schema).
Cài gói Newtonsoft.Json.Schema (Json.NET Schema)
Chạy trong terminal (thư mục project):
dotnet add package Newtonsoft.Json.Schema
Quan trọng: Newtonsoft.Json.Schema — là thư viện thương mại (với mục đích phi thương mại được dùng miễn phí). Rất phù hợp cho pet-project.
Ví dụ kiểm tra JSON có thỏa schema
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
string schemaJson = @"{
'type': 'object',
'properties': {
'id': { 'type': 'integer' },
'name': { 'type': 'string' },
'email': { 'type': 'string', 'format': 'email' }
},
'required': ['id', 'name']
}";
// Giả sử chúng ta có JSON như này
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// Trước tiên parse schema
JSchema schema = JSchema.Parse(schemaJson);
// Parse chính JSON vào JToken
JToken jsonObj = JToken.Parse(json);
// Kiểm tra xem JSON có hợp lệ theo schema không
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSON hợp lệ!");
}
else
{
Console.WriteLine("JSON KHÔNG hợp lệ!");
foreach (var error in errors)
Console.WriteLine(error);
}
Cách dùng kiểm tra JSON trong ứng dụng?
Thường: validate trước (IsValid), sau đó deserialize (JsonConvert.DeserializeObject<T>) và chỉ khi đó mới xử lý business logic. Điều này loại bỏ dữ liệu rác ngay từ đầu.
if (jsonObj.IsValid(schema))
{
// Mọi thứ OK, có thể deserialize
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// Dừng xử lý! Dữ liệu không hợp lệ.
}
5. Áp dụng JSON Schema trong thực tế
Khi nào cần validation?
- Khi nhận dữ liệu qua API (đặc biệt từ hệ thống bên ngoài và nhiều client khác nhau).
- Khi migrate dữ liệu giữa microservices và database.
- Khi sinh UI-form và auto-fill, nếu form được xây dựng dựa trên schema.
- Trong phỏng vấn: câu hỏi "nếu JSON sai thì sao?" xuất hiện khá thường.
Điểm thú vị cho người tò mò
- Kiểm tra format: "format": "email", "date-time".
- Quy tắc phức hợp: anyOf, oneOf, allOf.
- Validate object và array lồng nhau.
- Mở rộng schema theo nhu cầu riêng.
6. Ví dụ thực tế lớn
Dữ liệu vào (danh sách users):
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "chto eto?", "name": 123, "email": "not-an-email" }
]
Schema:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Kiểm tra trong .NET:
string jsonArray = @"[ ... ]"; // xem ở trên
string schemaJson = @"{ ... }"; // xem ở trên
JSchema schema = JSchema.Parse(schemaJson);
JToken arrayToken = JToken.Parse(jsonArray);
bool isValid = arrayToken.IsValid(schema, out IList<string> errs);
if (!isValid)
{
foreach (var error in errs)
Console.WriteLine(error);
// Ví dụ các thông báo:
// "String 'chto eto?' is not a valid integer."
// "Integer 123 is not a valid string."
// "String 'not-an-email' is not a valid email address."
}
7. Lỗi điển hình khi làm việc với JSON schema
Lỗi #1: schema không khớp với dữ liệu thực tế. Sau khi thay đổi model quên cập nhật schema. Validation sẽ bỏ sót lỗi hoặc chặn dữ liệu hợp lệ.
Lỗi #2: kiểu trong schema và model không trùng. Trường trong model đổi kiểu (ví dụ id thành string), nhưng schema vẫn là integer — validator sẽ báo lỗi.
Lỗi #3: thiếu trường bắt buộc cho business. Dù trường không được mark là bắt buộc, logic có thể phụ thuộc vào nó — thiếu sẽ gây crash.
Lỗi #4: format dữ liệu sai. Trường kiểu string chứa email hay datetime trông là string nhưng có thể không hợp lệ. Dùng format và/hoặc kiểm tra bổ sung.
Lỗi #5: implementation của thư viện lạc hậu so với chuẩn. Chuẩn thay đổi, các thư viện không phải lúc nào cũng kịp. Một vài kiểm tra có thể thiếu hoặc hoạt động khác.
GO TO FULL VERSION