• Thứ Hai, 28/11/2005 13:59 (GMT+7)

    Serialization trong .NET

    Serialization là một quá trình chuyển đối tượng (object) sang một hình thức khác, để sử dụng khi lưu trữ hoặc chuyển dữ liệu qua mạng. Bài viết này đề cập đến việc chuyển đối tượng sang dạng XML.

    XMLSerializer

    .Net Framework cung cấp các lớp trong namespace System.XML.Serialization cho công việc chuyển đổi này, trong đó lớp XMLSerializer có vai trò quan trọng. Mỗi thể hiện (instance) của lớp này được tạo ra cho mỗi đối tượng cần chuyển. Nó chứa các thông số ánh xạ để chuyển thuận hoặc chuyển ngược giữa đối tượng và dữ liệu XML. Lớp XMLSerializer có 2 phương thức quan trọng:

    public void Serialize(Stream, object);

    public object Deserialize(Stream);

    Chúng ta có thể sử dụng lớp XMLSerializer như sau:

    XMLSerializer serializer = new XMLSerializer(typeof(OrderedItem));

    OrderedItem order = new OrderedItem();

    ...

    //chuyển thuận

    XMLWriter writer = new XMLTextWriter(fs, new UTF8Encoding());

    serializer.Serialize(writer, order);

    //chuyển ngược

    fs = new FileStream(ten_file, FileMode.Open);

    XMLReader reader = new XMLTextReader(fs);

    i = (OrderedItem) serializer.Deserialize(reader);

    Mặc định XMLSerializer sẽ chuyển tất cả các trường (field) public, các thuộc tính (Property) read/write và các giá trị khác null trở thành element với tagname là tên thuộc tính. Tất cả các element này được đặt trong lớp element cha, với tagname là tên lớp. Ví dụ lớp Person được trình bày như dưới đây:

    public class Person

    {

    public string Name = "Ten";

    public int Age = 33;

    private string Address = "HCM";

    public string Sex = null;

    public Person()

    {

    }

    }

    sẽ được chuyển thành dữ liệu XML:

    <?XML version="1.0"?>

    <Person XMLns:xsd="http://www.w3.org/2001/XMLSchema" XMLns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <Name>Ten</Name>

    <Age>33</Age>

    </Person>

    Address không được chuyển vì nó là thuộc tính private, và Sex cũng không có trong dữ liệu XML vì có giá trị null.

    XMLSerializer có đầy đủ các chức năng để thực hiện việc chuyển các đối tượng sang dữ liệu XML và ngược lại. Sau đây, chúng ta sẽ khảo sát các khai báo cần thiết để chuyển các thuộc tính của một đối tượng sang dữ liệu XML.

    Chuyển các đối tượng

    Nếu bạn muốn chuyển lớp Person thành element với tagname là Employee thay vì Person, bạn định nghĩa nó thông qua thuộc tính XMLRoot:

    [XMLRoot("Employee")]

    public class Person

    {...}

    Nếu muốn chuyển thuộc tính Name thành element có TagName là PersonName, bạn định nghĩa nó thông qua XMLElement:

    [XMLElement("PersonName")]

    public string Name = "Ten";

    // tương tự thuộc tính Age thành element PersonAge

    [XMLAttribute("PersonAge")]

    public int Age = 33;

    Nếu không muốn chuyển một thuộc tính dạng public sang dữ liệu XML, ta dùng XMLIgnore:

    [XMLIgnore()]

    public string Sex = "Nam";

    Nếu các thuộc tính có kiểu là một lớp, các thành phần public của lớp được chuyển như đã định nghĩa, và element tương ứng của lớp sẽ có tagname được định nghĩa trong XMLElement.

    Lớp Person được chỉ định bởi:

    [XMLElement("Supervisor")]

    public Person Supervisor = null;

    sẽ được chuyển thành dữ liệu XML như sau:

    <Supervisor>

    <PersonName>Ten</PersonName>

    ...

    </Supervisor>

    XMLIgnore có thể giúp chúng ta chuyển hoặc không chuyển sang XML một trường hoặc thuộc tính tùy thuộc vào giá trị của nó.

    Để thực hiện việc này, chúng ta thêm một thuộc tính mới vào lớp với tên trùng với thuộc tính cộng thêm từ Specified sau đó. Ví dụ, chúng ta không muốn chuyển thuộc tính Age của lớp Person, chúng ta thực hiện như sau:

    [XMLAttribute("PersonAge")]

    public int Age = 33;

    [XMLIgnore()]

    public bool AgeSpecified = false;

    Nếu AgeSpecified có giá trị là true, Age sẽ được chuyển. Khi làm việc trên XMLSerializer tôi rất thích đặc điểm này vì nó giúp chúng ta bật tắt việc chuyển theo giá trị.

    Chuyển mảng, danh sách,...

    Ở phần trên chúng ta đã lần lượt xem xét các khai báo XML để chuyển các đối tượng đơn, trong phần này chúng ta sẽ khảo sát các khai báo cho các đối tượng tập hợp (Collection).

    Tương tự như ở phần trên, chúng ta cũng phải chuyển các đối tượng ở dạng mảng vào thành một element.

    [XMLArray("Agents")]

    [XMLArrayItem("Agent", typeof(Person))]

    public ArrayList Agents = null;

    Kết quả khi chuyển sẽ là:

    <Employee>

    <PersonName>Ten</PersonName>

    <Agents>

    <Agent>

    <PersonName>Ten</PersonName>

    </Agent>

    </Agents>

    </Employee>

    Ở ví dụ trên, các phần tử có kiểu là Person, đều được chuyển thành các element Agent. Nếu muốn thêm các kiểu khác vào danh sách, chúng ta thêm các khai báo XMLArayItem tương ứng tiếp sau khai báo cho Person.

    Nếu muốn tất cả các phần tử trong danh sách đều có tagname giống nhau, chúng ta cần khai báo XMLArrayItem với một tham số là ElementName (bỏ qua kiểu dữ liệu).

    Kết quả chúng ta có tập tin dữ liệu XML như sau:

    <Employee >

    <PersonName>Name</PersonName>

    <Agents>

    <Agent xsi:type="Person">

    <PersonName>Ten</PersonName>

    </Agent>

    <Agent xsi:type="Person1" />

    </Agents>

    </Employee>

    Thuộc tính xsi:type được thêm vào cho các element của ArrayList để giúp việc chuyển ngược dữ liệu XML này sang đối tượng.

    Trên đây tôi đã trình bày sơ lược về XMLSerializer. Nếu làm việc với đối tượng này, bạn sẽ phát hiện nhiều điều đặc biệt hơn nữa và hy vọng bạn sẽ thích thú. .NET Framework cũng cho phép bạn lấy các khai báo cho các lớp để chuyển thông qua viết lệnh. Nếu bạn đã từng sử dụng namespace System.Reflection trong .NET, hay reflection trong Java, chắc là sẽ cảm thấy thích thú với đặc điểm này.

    XMLSerializer có một hạn chế khi dự án có số lượng lớp quá lớn. Khi đó, việc tạo thể hiện cho hàng trăm lớp có khả năng làm chậm hệ thống. Tuy XMLSerializer cho phép bạn khai báo để nó chỉ tìm kiếm các đối tượng trong một số lớp cho trước, nhưng nó khá chậm. Theo kinh nghiệm của tôi khi làm việc trên dự án lớn, bạn chỉ nên tạo một thể hiện của XMLSerializer cho mỗi lớp và dùng nó xuyên suốt toàn bộ ứng dụng thì vấn đề tốc độ thực thi sẽ được giải quyết.

    Huỳnh Bảo Toàn
    Paragon Solutions Vietnam (PSV)

    ID: A0511_121