1. Giới thiệu
Chúng ta đã có thời gian làm việc với collection. Không có chúng thì khó sống cả trong lập trình lẫn đời thường. Hãy tưởng tượng bạn làm một danh sách việc cần làm trong ngày: mua đồ, gọi bác sĩ, lấy đơn hàng. Bạn sẽ không mở một cuốn sổ riêng cho mỗi việc. Dễ và hợp lý hơn là ghi tất cả vào một danh sách.
Trong lập trình cũng vậy: khi có nhiều đối tượng — users, orders, messages — ta không tạo biến riêng cho từng cái. Thay vào đó dùng collection: list, dictionary, set. Điều này giúp lưu trữ, duyệt và xử lý một nhóm dữ liệu cùng lúc dễ dàng.
Tình huống thực tế:
- Lưu trữ và trao đổi dữ liệu giữa các ứng dụng: bộ sưu tập sách của bạn có thể di chuyển từ chương trình này sang chương trình khác.
- Cache bộ dữ liệu lên đĩa.
- Truyền dữ liệu qua mạng (frontend ↔ backend).
- Import/export thông tin (ví dụ bạn muốn làm export sách sang JSON!).
Trong phỏng vấn: “Bạn sẽ serialize và lưu danh sách orders như thế nào?” — sẽ ấn tượng nếu bạn nhắc tới không chỉ object đơn lẻ mà còn serialization của collection!
2. Collection hoạt động thế nào khi serialize
JSON và collection: một chuyện tình ngắn
Khi bạn serialize một object bình thường, nó trở thành JSON object dạng { "field": value }. Còn khi serialize list hoặc array thì sẽ thành JSON array [ ... ].
Minh họa:
| C# | JSON |
|---|---|
|
|
|
|
|
|
Phép màu chính: Gọi JsonSerializer.Serialize() cho collection — nó tự động biến thành mảng! Ngược lại Deserialize<List<T>>() — và phép màu xảy ra lần nữa.
Tương ứng giữa collection C# và mảng JSON
| Kiểu collection trong C# | Ví dụ | JSON |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Ví dụ: serialize và deserialize mảng sách
Bắt đầu với ví dụ tối giản. Lấy lớp Book mà ta đã dùng ở bài trước:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
Bây giờ tạo mảng sách, serialize nó, lưu vào file và load lại.
using System;
using System.IO;
using System.Text.Json;
namespace LibraryApp
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
class Program
{
static void Main()
{
// Sozdaem massiv knig
Book[] books = new Book[]
{
new Book { Title = "Tri tovarishcha", Author = "Erikh Mariya Remark" },
new Book { Title = "Master i Margarita", Author = "Mikhail Bulgakov" },
new Book { Title = "1984", Author = "Dzhordzh Oruell" }
};
// Serializatsiya massiva knig v stroku
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(books, options);
Console.WriteLine("JSON massiva knig:\n" + json);
// Zapic v fail (sinkhronno)
File.WriteAllText("books.json", json);
// Chtenie iz faila (sinkhronno)
string jsonFromFile = File.ReadAllText("books.json");
// Deserializatsiya obratno v massiv
Book[]? booksFromFile = JsonSerializer.Deserialize<Book[]>(jsonFromFile);
// Vivodim rezultat na konsol
Console.WriteLine("\nDeserializovannye knigi:");
if (booksFromFile != null)
{
foreach (var book in booksFromFile)
{
Console.WriteLine($"- {book.Title} (avtor: {book.Author})");
}
}
}
}
}
Chuyện gì đang xảy ra?
- Ta tạo mảng Book[], điền ba cuốn sách.
- Dùng JsonSerializer.Serialize để biến mảng thành JSON string đẹp (tùy chọn WriteIndented làm cho format dễ đọc).
- Ghi vào file, đọc lại và deserialize — thế là ta có lại mảng sách!
- Kiểm tra xem dữ liệu đã được phục hồi chính xác.
Kiểm tra xem file "books.json" xuất hiện trong thư mục chương trình. Mở nó ra, sẽ thấy JSON kiểu như sau:
[
{
"Title": "Tri tovarishcha",
"Author": "Erikh Mariya Remark"
},
{
"Title": "Master i Margarita",
"Author": "Mikhail Bulgakov"
},
{
"Title": "1984",
"Author": "Dzhordzh Oruell"
}
]
4. Ví dụ: serialize collection List<T>
Làm việc với List<Book> không có gì khác biệt. Serializer nhìn thấy collection và biến nó thành JSON array.
List<Book> myBooks = new List<Book>
{
new Book { Title = "Prestuplenie i nakazanie", Author = "Fyodor Dostoevskiy" },
new Book { Title = "Voyna i mir", Author = "Lev Tolstoy" }
};
string jsonList = JsonSerializer.Serialize(myBooks, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonList);
// A deserializuem vot tak:
List<Book>? loadedBooks = JsonSerializer.Deserialize<List<Book>>(jsonList);
// Proveryaem, chto vse vernulos!
foreach (var book in loadedBooks!)
{
Console.WriteLine($"{book.Title} ({book.Author})");
}
Có thể serialize list số nguyên đơn giản không?
Tất nhiên rồi! Serialization không chỉ cho class của bạn mà còn cho kiểu nguyên thủy:
List<int> numbers = new List<int> { 10, 20, 30, 40 };
string jsonNums = JsonSerializer.Serialize(numbers); // rezultat: [10,20,30,40]
List<int>? loadedNums = JsonSerializer.Deserialize<List<int>>(jsonNums);
// loadedNums: List<int> c temi zhe znacheniyami
5. Sơ đồ và tương tự: serializer nhìn collection ra sao?
Để hiểu rõ hơn cách serializer xử lý collection, nhìn sơ đồ đơn giản này:
graph TD;
A[List[Book] v C#] -->|Serialize| B[JSON-massiv v stroke]
B -->|Write| C[Fail books.json]
C -->|Read| D[Stroka JSON iz faila]
D -->|Deserialize| E[List[Book] v C#]
Tóm tắt quá trình:
- Collection hoặc array được serialize thành mảng trong JSON ([ ... ]).
- Thứ tự phần tử được giữ nguyên (trừ khi bạn cấu hình khác).
- Mỗi phần tử trong collection được serialize thành object riêng.
6. Những điểm cần lưu ý khi serialize collection
1. null và collection rỗng
Nếu collection là null, serializer theo mặc định sẽ ghi null vào JSON. Cẩn thận nếu trong business logic của bạn collection rỗng và null không đồng nghĩa nhau!
Nếu collection rỗng (new List<Book>()), JSON sẽ là [] — mảng rỗng. Điều này tiện khi muốn rõ ràng thể hiện không có phần tử.
2. Deserialize — thứ tự quan trọng
Thứ tự phần tử trong mảng luôn được giữ. Nghĩa là nếu bạn serialize ba cuốn sách theo một thứ tự, khi deserialize sẽ nhận lại cùng thứ tự đó.
3. Collection chứa object khác loại?
System.Text.Json không hỗ trợ polymorphism “ngoài hộp”. Nếu bạn có ví dụ List<Animal> chứa Dog và Cat ( mỗi cái là lớp dẫn xuất ), thì mặc định không thể biết khi deserialize đó là loại cụ thể nào. Vấn đề polymorphic serialization sẽ được bàn sau, nhưng với list bình thường — mọi thứ đơn giản.
7. Lỗi hay gặp và hiểu lầm phổ biến
Lỗi #1: Serialize collection nhưng quên là có thể có field private
JsonSerializer chỉ serialize các property public (và có getter/setter public). Nếu class của bạn như sau:
public class User
{
public string Login { get; set; }
private string Password { get; set; } // Ne budet serializovano!
}
Password sẽ không vào JSON, và điều đó tốt cho bảo mật. Nhưng nếu bạn muốn lưu nó — nhớ làm property public.
Lỗi #2: Serialize collection có phần tử null
Nếu collection chứa null, ví dụ: new List<Book> { null, book2 }, thì sau khi serialize phần tử đầu sẽ là null. Khi deserialize — cũng giống vậy. Ví dụ JSON:
[null, { "Title": "Voyna i mir", "Author": "Lev Tolstoy" }]
Thực tế ít gặp nhưng nếu collection bạn serialize có “lỗ hổng” thì hãy xử lý trong logic.
Lỗi #3: Kiểu không đúng khi deserialize
Lỗi do viết nhầm: serialize ra danh sách nhưng deserialize thành mảng (hoặc ngược lại). Thường thì nếu kiểu tương thích (Book[] ↔ List<Book>) thì vẫn ổn, nhưng có những tình huống phức tạp hơn (ví dụ deserialize mảng string thành object) sẽ gây lỗi.
GO TO FULL VERSION