CodeGym /Các khóa học /C# SELF /Đọc và ghi XML: XmlSeriali...

Đọc và ghi XML: XmlSerializer

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

1. Giới thiệu

XML (eXtensible Markup Language) — ngôn ngữ đánh dấu mở rộng, một định dạng phổ biến để lưu trữ và trao đổi dữ liệu có cấu trúc phức tạp. Khác với JSON, XML không ngắn gọn bằng, nhưng vẫn được dùng nhiều trong ngân hàng, trao đổi giữa dịch vụ nhà nước, tài liệu và tích hợp với 1C, SAP, Oracle, v.v. Một số dữ liệu trong Windows và .NET (ví dụ configs) vẫn lưu ở định dạng XML.

Về mặt trực quan XML giống HTML, chỉ là không có bộ thẻ cố định như <body> hay <div> — bạn tự định nghĩa cấu trúc các phần tử. Ví dụ:

<Person>
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</Person>

Những điều cơ bản về XML và mapping sang đối tượng C#

Khác với JSON, XML không có các kiểu dữ liệu đặc biệt cho mảng hay boolean — mọi thứ được biểu diễn bằng elements và attributes. Danh sách thường hiện thực bằng các element lặp với cùng tên.

Để làm việc tiện với XML trong C#, ta dùng cơ chế serialization/deserialization qua XmlSerializer.

Serialization — biến một đối tượng C# thành chuỗi XML. Deserialization — quá trình ngược lại: lấy đối tượng C# từ XML.

Nó hoạt động "out of the box" nếu class của bạn thỏa một số yêu cầu — sẽ nói chi tiết ở dưới.

Tại sao dùng XmlSerializer?

XmlSerializer — công cụ chuẩn của .NET (namespace System.Xml.Serialization) để chuyển objects sang XML và ngược lại. Nó đơn giản, type-safe và phù hợp cho import/export dữ liệu, configs, trao đổi giữa hệ thống và tích hợp với dịch vụ nhà nước.

2. Ví dụ: serialize một đối tượng

Giả sử ta có model người dùng:

public class User
{
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public int Age          { get; set; }
}

Serialize một đối tượng thành XML:

using System.Xml.Serialization;

User user = new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 };
// Để serialize cần kiểu
XmlSerializer serializer = new XmlSerializer(typeof(User));

// Ghi XML vào file
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);

Điều gì xảy ra?

- Tạo một instance của class User.
- Tạo XmlSerializer cho kiểu User.
- Mở file bằng FileStream.
- Gọi phương thức Serialize(), nó chuyển object thành XML và ghi vào file.

Nội dung file user.xml:

<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</User>

Đơn giản vậy — ít code và có ngay XML.

3. Deserialization: đọc XML trở lại thành object

Đọc file XML vừa tạo và phục hồi object C#:

// Mở file XML
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, tuổi: {loadedUser?.Age}");

Phương thức Deserialize() trả về object, nên ta cast về User (dùng as). Nếu file hợp lệ và khớp model — object sẽ được phục hồi đầy đủ.

4. Cách hoạt động của serialization: những lưu ý thường gặp

Serialization sang XML yêu cầu tuân theo vài quy tắc:

• Class để serialize nên có constructor public mặc định (không tham số).

• Các property bị serialize phải là public: cần cả getter và setter. Các thành viên private bị bỏ qua.

• Các property chỉ đọc, static hoặc abstract không hoạt động.

Nếu vi phạm, bạn sẽ gặp InvalidOperationException với thông báo kiểu "XmlSerializer cannot serialize this type".

5. Serialize collections: mảng và danh sách

Nếu cần serialize nhiều user — bọc bằng một class bao quanh:

public class UserList
{
    public List<User> Users { get; set; } = new List<User>();
}

Tạo collection và serialize:

UserList users = new UserList
{
    Users = new List<User>
    {
        new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 },
        new User { FirstName = "Maria", LastName = "Ivanova", Age = 25 }
    }
};

XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);

Sẽ nhận được XML:

<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Users>
    <User>
      <FirstName>Ivan</FirstName>
      <LastName>Petrov</LastName>
      <Age>30</Age>
    </User>
    <User>
      <FirstName>Maria</FirstName>
      <LastName>Ivanova</LastName>
      <Age>25</Age>
    </User>
  </Users>
</UserList>

Có thể serialize trực tiếp List<User>? Có, nhưng phần tử gốc sẽ tên là ArrayOfUser, điều này không phải lúc nào cũng phù hợp cho tích hợp.

6. Attributes để điều khiển serialization

Bằng attributes bạn điều chỉnh định dạng XML: đổi tên elements, serialize thành attributes, bỏ qua trường, v.v. Các attribute hữu dụng: [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].

Serialize thành attributes

Ghi tên và họ thành attributes, tuổi là element:

public class User
{
    [XmlAttribute]
    public string FirstName { get; set; }

    [XmlAttribute]
    public string LastName { get; set; }

    [XmlElement]
    public int Age { get; set; }
}

Kết quả:

<User FirstName="Ivan" LastName="Petrov">
  <Age>30</Age>
</User>

Điều khiển tên element

Đổi tên tag thành tên dễ đọc hơn:

public class User
{
    [XmlElement("Tên")]
    public string FirstName { get; set; }

    [XmlElement("Họ")]
    public string LastName { get; set; }

    [XmlElement("Tuổi")]
    public int Age { get; set; }
}

XML sinh ra:

<User>
  <Tên>Ivan</Tên>
  <Họ>Petrov</Họ>
  <Tuổi>30</Tuổi>
</User>

Bỏ qua property

Nếu không muốn serialize một property — gắn cho nó [XmlIgnore]:

public class User
{
    public string FirstName { get; set; }

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

7. Serialize hierarchy và nested objects

Các lớp lồng nhau dễ chuyển thành các element lồng nhau trong XML:

public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

public class User
{
    public string FirstName { get; set; }
    public Address Address { get; set; }
}
var user = new User
{
    FirstName = "Anna",
    Address = new Address { City = "Neongrad", Street = "Vyazovaya" }
};

XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);

XML:

<User>
  <FirstName>Anna</FirstName>
  <Address>
    <City>Neongrad</City>
    <Street>Vyazovaya</Street>
  </Address>
</User>

8. Serialize vào / từ chuỗi (không dùng file)

Serialize vào chuỗi

using System.Text;

var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();

serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);

Deserialize từ chuỗi

var xml = "<User><FirstName>Anna</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);

9. Ví dụ: xuất nhập dữ liệu ứng dụng

Xuất/nhập danh sách người dùng ra file XML:

public static void ExportUsers(UserList users, string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Create);
    serializer.Serialize(fs, users);
}

public static UserList ImportUsers(string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Open);
    return (UserList)serializer.Deserialize(fs);
}

Ứng dụng: backup, trao đổi với service khác, tích hợp. Trong phỏng vấn thường có câu hỏi về XmlSerializer — đặc biệt ở các công ty làm với dịch vụ nhà nước và ngân hàng.

10. Lỗi thường gặp và lưu ý khi làm việc

Nếu field không là public, nó sẽ không được serialize.

Property chỉ có getter (read-only) sẽ bị bỏ qua.

Property kiểu interface hoặc abstract class thì serializer không hỗ trợ.

Kiểu DateTime được serialize theo định dạng XSD (xem tài liệu).

Collection là mảng và List<T> hoạt động như mong đợi, còn với ObservableCollection<T> có thể gây bất ngờ với hệ thống bên ngoài.

Lưu ý về encoding: khi ghi bằng StreamWriter mà không chỉ định encoding rõ ràng thì kết quả có thể là UTF-16; để tương thích thường mong đợi UTF-8 — hãy chỉ định encoding rõ ràng.

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Serialization danh sách đối tượng
Serialization danh sách đối tượng
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION