CodeGym /Các khóa học /C# SELF /Quản lý serialization của các collection

Quản lý serialization của các collection

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

1. Giới thiệu

Dù muốn tin rằng collection được serialize và deserialize hoàn hảo "out of the box", trong dự án thực tế thì thường không như vậy. Đôi khi cần ẩn một số collection khỏi serialization — ví dụ dữ liệu cache nội bộ. Có khi cần đổi tên thuộc tính collection để phù hợp với contract API. Trong vài trường hợp quan trọng là bạn muốn kiểm soát phần tử nào được lưu hoặc bỏ qua, hoặc thậm chí biến đổi collection theo cách đặc biệt để JSON cuối cùng trông dễ hiểu và "human-readable" cho các service khác.

May mắn là System.Text.Json cung cấp cách đơn giản và rõ ràng để quản lý serialization thông qua các attribute, có thể áp dụng cho collection cũng như từng phần tử. Trong phần này chúng ta sẽ phát triển tiếp model thư viện để hiểu cách hoạt động trong thực tế.

2. Loại trừ thuộc tính collection: [JsonIgnore]

Bắt đầu từ điều cơ bản. Đôi khi trong class của bạn có collection không nên serialize — ví dụ dữ liệu tạm thời, cache hoặc nhạy cảm. Làm sao? Dùng [JsonIgnore]!

Giả sử ta có class Library với thuộc tính List<Book> Cache, chỉ dùng để truy cập nhanh:

using System.Text.Json.Serialization;

public class Library
{
    public string Name { get; set; }

    public List<Book> Books { get; set; }

    [JsonIgnore]
    public List<Book> Cache { get; set; } // Không được serialize!
}
// Ví dụ sử dụng:
var library = new Library
{
    Name = "Thư viện chính",
    Books = new List<Book>
    {
        new Book { Title = "Thung lũng kỳ diệu", Author = new Author { Name = "Tuve Janssen", BirthYear = 1914 } }
    },
    Cache = new List<Book>
    {
        new Book { Title = "Chúa ruồi", Author = new Author { Name = "William Golding", BirthYear = 1911 } }
    }
};

string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json); // Trong JSON không có thuộc tính Cache!

Kết quả serialization sẽ khoảng như sau:

{
  "Name": "Thư viện chính",
  "Books": [
    {
      "Title": "Thung lũng kỳ diệu",
      "Author": {
        "Name": "Tuve Janssen",
        "BirthYear": 1914
      }
    }
  ]
}

Thấy không? Không có "cache" ra bên ngoài. Mọi thứ dưới [JsonIgnore] — bị ẩn và an toàn, giống như mật khẩu Wi-Fi nằm trong đầu bạn.

3. Đổi tên collection bằng [JsonPropertyName]

Thường gặp API yêu cầu, ví dụ, "items" thay vì "Books"? Hoặc bạn không muốn đổi tên field trong C# (để không rối), nhưng trong JSON nó phải "nghe" khác?

Thực hiện như sau:

using System.Text.Json.Serialization;

public class Library
{
    public string Name { get; set; }

    [JsonPropertyName("items")]
    public List<Book> Books { get; set; }

    [JsonIgnore]
    public List<Book> Cache { get; set; }
}
// Serialize:
var library = new Library { Name = "Chi nhánh #1", Books = new List<Book>() };
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

Output:

{
  "Name": "Chi nhánh #1",
  "items": []
}

Lưu ý rằng việc deserialize cũng sẽ map đúng trường items từ JSON vào Books trong C# — cơ chế hoạt động hai chiều.

4. Quản lý serialization của collection và các phần tử

Để làm điều này bạn sẽ dùng JsonIgnoreCondition.WhenWritingNull và/hoặc nullable.

Có trường hợp collection là field optional. Ví dụ thư viện mới tạo chưa có sách. Nếu bạn không muốn JSON chứa thuộc tính books: null, bạn có thể kiểm soát bằng options:

var library = new Library { Name = "Thư viện trống" };
// Books không được khởi tạo = null

var options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    WriteIndented = true
};

string json = JsonSerializer.Serialize(library, options);
Console.WriteLine(json);

Kết quả:

{
  "Name": "Thư viện trống"
}

Còn nếu bạn có danh sách rỗng (nhưng không phải null), serializer sẽ xuất "books": []. Đây là sự khác biệt quan trọng, vì đôi khi bạn muốn ẩn field khi nó là null, nhưng không ẩn khi nó là danh sách rỗng.

5. Attribute [JsonIgnore] trên các thuộc tính của phần tử

Các attribute serialization cũng hoạt động bên trong phần tử của collection. Bạn có thể ẩn các thuộc tính riêng lẻ của mỗi object trong list.

public class Book
{
    public string Title { get; set; }
    public Author Author { get; set; }

    [JsonIgnore]
    public string InternalCode { get; set; }
}

Giờ khi serialize một cuốn sách trong collection Books, trường InternalCode sẽ không xuất hiện trong JSON.

6. Địa chỉ hóa collection qua "index" hoặc cấu trúc lồng nhau

Đôi khi cần serialize collection không chỉ như mảng, mà ví dụ như "map" (dictionary) — nếu mỗi cuốn sách có id duy nhất. Trong trường hợp này — không cần attribute phức tạp cho phần tử, mà dùng cách chuẩn — khai báo property dạng dictionary:

public class Library
{
    [JsonPropertyName("catalog")]
    public Dictionary<string, Book> BookCatalog { get; set; }
}

Khi serialize, dictionary sẽ thành object với cặp key-value:

var library = new Library
{
    BookCatalog = new Dictionary<string, Book>
    {
        ["978-5-699-12345-6"] = new Book { Title = "Từ điển", Author = new Author { Name = "Không rõ", BirthYear = 2000 } }
    }
};

JSON:

{
  "catalog": {
    "978-5-699-12345-6": {
      "Title": "Từ điển",
      "Author": {
        "Name": "Không rõ",
        "BirthYear": 2000
      }
    }
  }
}

Cách biểu diễn này tiện khi gửi qua API, nơi cần giữ liên kết giữa key và object.

7. Lỗi và khó khăn khi quản lý serialization của collection

Nếu cố serialize collection mà không phải tất cả phần tử đều được khởi tạo đúng (ví dụ trong list có null), mặc định System.Text.Json sẽ ghi những phần tử đó là null trong mảng.

Ngay cả khi bạn đặt DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, các phần tử-null bên trong mảng vẫn tồn tại — quy tắc áp dụng cho thuộc tính của object, không phải nội dung collection. Để tránh, bạn nên dọn collection trước: RemoveAll(b => b == null).

Một nhầm lẫn thường gặp khi deserialize là tên không khớp. Nếu quên khai báo [JsonPropertyName], class sẽ chờ thuộc tính Books, còn bạn gửi JSON với items: kết quả collection sẽ không được điền và vẫn rỗng. Luôn kiểm tra tên đúng!

8. Bảng: các attribute chính áp dụng ở đâu

Attribute Có áp dụng cho collection được không? Có áp dụng cho phần tử của collection được không? Ví dụ sử dụng
[JsonIgnore]
Ẩn list hoặc field bên trong Book
[JsonPropertyName]
Đổi tên Books → items hoặc Title → name
[JsonInclude]
Bao gồm thuộc tính private trong serialization
[JsonConverter]
Gán converter đặc biệt cho list

9. Sơ đồ serialization của collection có attribute


+-------------+
|   Library   |
+-------------+
  | Name           -- serialize thành "Name"
  | Books          -- [JsonPropertyName("items")], serialize thành "items": [...]
  | Cache          -- [JsonIgnore], không serialize
  | BookCatalog    -- [JsonPropertyName("catalog")], serialize thành "catalog": {...}
Kết quả JSON khoảng như sau:
{
  "Name": "Thư viện thành phố",
  "items": [
    {
      "Title": "1984",
      "Author": {
        "Name": "George Orwell",
        "BirthYear": 1903
      }
    },
    {
      "Title": "Great Expectations",
      "Author": {
        "Name": "Charles Dickens",
        "BirthYear": 1812
      }
    }
  ],
  "catalog": {
    "978-1234567890": {
      "Title": "The Call of Cthulhu",
      "Author": {
        "Name": "Howard Phillips Lovecraft",
        "BirthYear": 1890
      }
    }
  }
}

10. Giá trị thực tiễn và điểm lưu ý trong phỏng vấn và dự án thực tế

Trong môi trường "thực chiến" luôn phải cân nhắc contract của API bên ngoài và yêu cầu về serialization. Cần biết cách "giấu" collection nội bộ, tuân theo chữ hoa/chữ thường và style tên, thỉnh thoảng còn phải thay đổi schema serialization động tùy phiên bản client.

Các câu hỏi thường gặp trong phỏng vấn:

  • Làm sao chỉ serialize một phần dữ liệu?
  • Làm sao để thuộc tính collection không xuất hiện trong JSON?
  • Làm sao map tên thuộc tính giữa C# và JSON khi khác nhau?
  • Có thể ẩn riêng một vài phần tử trong collection khỏi serialization (ví dụ thông tin nhạy cảm) không?

Câu trả lời thường xoay quanh việc dùng đúng các attribute và option serialization: [JsonIgnore], [JsonPropertyName], tùy chọn JsonSerializerOptions và xử lý nội dung collection có suy tính.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Serialization các collection
Serialization các object lồng nhau và có cấu trúc phân cấp
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION