.Net – Serialization và Deserialization trong C#

Serialization là một quá trình để chuyển đổi một cấu trúc dữ liệu hoặc đối tượng thành một định dạng có thể lưu trữ được (ví dụ như trong một file, bộ nhớ, hoặc vận chuyển thông qua mạng), sau đó nó có thể được phục hồi để trở lại trạng thái ban đầu trong một môi trường khác thông qua quá trình deserialization. Rất nhiều ngôn ngữ lập trình hiện nay hỗ trợ kĩ thuật này bao gồm C#, Java, Objective-C, Perl, Python, Ruby, PHP,… (Wikipedia).

Serialization Process 

Attribute [Serializable] và [NonSerialized]

Mọi đối tượng muốn được serialize đều phải được khai báo kèm theo attribute [Serializable]. Ngoài ra, mọi kiểu dữ liệu được sử dụng trong đối tượng cũng phải tuân theo quy tắc này.

Nếu bạn muốn loại trừ một thành phần (method, field, property,…) không muốn được serialize, bạn có thể đánh dấu chúng bằng attribute [NonSerialized].

Thông thường hai attribute này là đủ để bạn thực hiện quá trình serialization và deserialization. Tuy nhiên nếu cần điều khiển thủ công hai quá trình này, bạn cần đến interface ISerializable. Ngược lại, nếu không quan tâm đến điều này bạn có thể bỏ qua phần ngay sau đây.

Interface ISerializable

Để điều khiển quá trình serialize một cách thủ công, bạn cần hiện thực interface ISerializable trong namespace System.Runtime.Serialization. Interface này chứa phương thức GetObjectData() mà quá trình serialize sẽ gọi đến khi được thực thi:

 
void GetObjectData(
         SerializationInfo info,
         StreamingContext context
)

Tham số SerializationInfo để lưu trữ thông tin đối tượng. Lớp này cung cấp các phương thức Add và Get để gán và lấy thông tin từ đối tượng.

Ngoài ra bạn cần cung cấp thêm một constructor của đối tượng với hai tham số tương tự phương thức GetObjectData() là SerializationInfo và StreamingContext dùng cho quá trình deserialize. Khi bạn hiện thực giao diện này thì attribute [NonSerialized] sẽ không có hiệu lực.

Ví dụ đơn giản cho lớp MyFile mà bạn có thể thực hiện kiểm tra với phương thức Main() được cung cấp ở phần cuối.

[Serializable]
public class MyFile:ISerializable
{
    public string Name;
    [NonSerialized]
    public long Length;

    public MyFile(string name,long length)
    {
        Name=name;
        Length=length;
    }

    private MyFile(SerializationInfo info, StreamingContext context)
    {
        Name=info.GetString("Name");
        Length=info.GetInt64("Length");
    }
    public override string ToString()
    {
        return string.Format("[MyFile Name={0}, Length={1}]", Name, Length);
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Name",Name,Name.GetType());
        info.AddValue("Length",Length,Length.GetType());
    }
}

Các loại formatter

Các formatter được sử dụng để serialize trong .Net đều được implement interface IFormatter trong namespace System.Runtime.Serialization, bao gồm BinaryFormatter và SoapFormatter. Interface này chỉ có hai phương thức là Serialize() và Deserialize()

–          BinaryFormatter: (namespace System.Runtime.Serialization.Formatters.Binary)

Serialize đối tượng thành một tập tin nhị phân.

–          SoapFormatter: (namespace System.Runtime.Serialization.Formatters.Soap)

Serialize đối tượng thành định dạng XML để truyền tải thông tin giữa các ứng dụng qua mạng thông qua giao thức HTTP. Do là dạng văn bản nên dung lượng dữ liệu tạo ra sẽ nặng hơn so với BinaryFormatter.

Note: Nếu bạn muốn một phương pháp Serialize để tạo file Xml thông thường, hãy tìm hiểu namespace System.Xml.Serialization.

Ví dụ minh họa

Để làm ví dụ, trước tiên tôi tạo ra hai lớp sau dùng để lưu trữ các file trong một folder. Hai class tương ứng là MyFolder và MyFile đều được đánh dấu với [Serializable].

[Serializable]
public class MyFolder
{
    public string Path;
    public IDictionary<string, MyFile> Files;

    public MyFolder(string path)
    {
        Files=new Dictionary<string, MyFile>();
        Path=path;
        foreach(var item in Directory.GetFiles(path))
        {
            FileInfo finfo=new FileInfo(item);
            Files.Add(finfo.Name,new MyFile(finfo.Name,finfo.Length));
        }
    }
    public bool ContainsFile(string name)
    {
        return Files.ContainsKey(name);
    }
}
[Serializable]
public class MyFile
{
    public string Name;
    public long Length;

    public MyFile(string name,long length)
    {
        Name=name;
        Length=length;
    }
    public override string ToString()
    {
        return string.Format("[MyFile Name={0}, Length={1}]", Name, Length);
    }
}

Tiếp đến ta cần hai phương thức để Serialize và Deserialize, hai phương thức này được viết chung để hỗ trợ cho mọi kiểu formatter implement từ IFormatter, các phương thức này được đặt trong lớp Y2Formatter:

private static void Serialize(string fileName, object data, IFormatter formatter)
{
    FileStream myStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);

    formatter.Serialize(myStream , data);

    myStream .Close();
}

private static T Deserialize<T>(string fileName, IFormatter formatter)
{
    FileStream myStream = new FileStream(fileName, FileMode.Open);

    T data= (T)formatter.Deserialize(myStream);

    myStream.Close();

    return data;
}

Cuối cùng cung cấp thêm các phương thức XXXSerialize() và XXXDeserialize() tương ứng cho từng kiểu formatter để đơn giản hóa lời gọi phương thức.

public static void BinSerialize(string fileName, object data)
{
    Serialize(fileName,data,new BinaryFormatter());
}

public static T BinDeserialize<T>(string fileName)
{
    return Deserialize<T>(fileName,new BinaryFormatter());
}

public static void SoapSerialize(string fileName, object data)
{
    Serialize(fileName,data,new SoapFormatter());
}

public static T SoapDeserialize<T>(string fileName)
{
    return Deserialize<T>(fileName,new SoapFormatter());
}

Phương thức Main để kiểm tra:

public static class Program
{
    private const string FILE_NAME=@"C:\data.xml";
    static void Main(string[] args)
    {
        // Test BinSerialize(), BinDeserialize()
        //
        // MyFolder firstFolder=new MyFolder(@"C:\Windows");
        // Y2Formatter.BinSerialize(FILE_NAME,firstFolder);
        // MyFolder secondFolder=Y2Formatter.BinDeserialize<MyFolder>(FILE_NAME);
        // foreach(var item in secondFolder.Files)
        //      Console.WriteLine(item.Value.Name);

        // Test SoapSerialize(), SoapDeserialize()
        //
        MyFile file=new MyFile("Fake.abc",10);
        Y2Formatter.SoapSerialize(FILE_NAME,file);

        MyFile file2=Y2Formatter.SoapDeserialize<MyFile>(FILE_NAME);

        Console.WriteLine(file2);

        Console.Read();
    }
}

Output:

[MyFile Name=Fake.abc, Length=10]

Bạn có thể thấy file data.xml kết quả của ví dụ trên với nội dung sau:

<SOAP-ENV:Envelope xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema&#8221; xmlns:SOAP-ENC=”http://schemas.xmlsoap.org/soap/encoding/&#8221; xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/&#8221; xmlns:clr=”http://schemas.microsoft.com/soap/encoding/clr/1.0&#8243; SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/”&gt;

<SOAP-ENV:Body>

<a1:MyFile id=”ref-1″ xmlns:a1=”http://schemas.microsoft.com/clr/nsassem/SerializationExample/AppDomainExample%2C%20Version%3D1.0.4170.15013%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull”&gt;
<Name id=”ref-3″>Fake.abc</Name>
<Length>10</Length>
</a1:MyFile>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Mã nguồn hoàn chỉnh

Program.cs:

using System;
using System.Collections.Generic;
using System.IO;

namespace SerializationExample{

    [Serializable]
    public class MyFolder
    {
        public string Path;
        public IDictionary<string, MyFile> Files;

        public MyFolder(string path)
        {
            Files=new Dictionary<string, MyFile>();
            Path=path;
            foreach(var item in Directory.GetFiles(path))
            {
                FileInfo finfo=new FileInfo(item);
                Files.Add(finfo.Name,new MyFile(finfo.Name,finfo.Length));
            }
        }
        public bool ContainsFile(string name)
        {
            return Files.ContainsKey(name);
        }
    }
    [Serializable]
    public class MyFile
    {
        public string Name;
        public long Length;

        public MyFile(string name,long length)
        {
            Name=name;
            Length=length;
        }
        public override string ToString()
        {
            return string.Format("[MyFile Name={0}, Length={1}]", Name, Length);
        }
    }

    public static class Program
    {
        private const string FILE_NAME=@"C:\data.xml";
        static void Main(string[] args)
        {
            // Test BinSerialize(), BinDeserialize()
            //
            // MyFolder firstFolder=new MyFolder(@"C:\Windows");
            // Y2Formatter.BinSerialize(FILE_NAME,firstFolder);
            // MyFolder secondFolder=Y2Formatter.BinDeserialize<MyFolder>(FILE_NAME);
            // foreach(var item in secondFolder.Files)
            //      Console.WriteLine(item.Value.Name);

            // Test SoapSerialize(), SoapDeserialize()
            //
            MyFile file=new MyFile("Fake.abc",10);
            Y2Formatter.SoapSerialize(FILE_NAME,file);

            MyFile file2=Y2Formatter.SoapDeserialize<MyFile>(FILE_NAME);

            Console.WriteLine(file2);

            Console.Read();
        }
    }
}

Y2Formatter.cs:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;

namespace SerializationExample
{
    public class Y2Formatter
    {
        public static void BinSerialize(string fileName, object data)
        {
            Serialize(fileName,data,new BinaryFormatter());
        }
        public static T BinDeserialize<T>(string fileName)
        {
            return Deserialize<T>(fileName,new BinaryFormatter());
        }
        public static void SoapSerialize(string fileName, object data)
        {
            Serialize(fileName,data,new SoapFormatter());
        }
        public static T SoapDeserialize<T>(string fileName)
        {
            return Deserialize<T>(fileName,new SoapFormatter());
        }

        private static void Serialize(string fileName, object data, IFormatter formatter)
        {
            FileStream myStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);

            formatter.Serialize(myStream , data);

            myStream .Close();
        }
        private static T Deserialize<T>(string fileName, IFormatter formatter)
        {
            FileStream myStream = new FileStream(fileName, FileMode.Open);

            T data= (T)formatter.Deserialize(myStream);

            myStream.Close();

            return data;
        }
    }
}

https://yinyangit.wordpress.com

Advertisements

17 thoughts on “.Net – Serialization và Deserialization trong C#

      • Mọi đối tượng đều có thể được serialize miễn là nó được đặt attribute [Serializable]. Còn self-modifying code thì mình chưa từng đề cập tới trong blog này. Bạn có thể hiểu đây là khả năng tự động thay đổi nội dung (code) của một chương trình khi thực thi.

        Thông thường nhất là javascript, bạn có thể viết các đoạn mã trong một trang web để tạo thêm các script, function và thực thi chúng khi một trang này được chạy trên trình duyệt.

        Trong .NET bạn có thể tìm hiểu về CodeDom hoặc Reflection Emit để thực hiện được kĩ thuật này.

  1. Pingback: Làm việc với Disconnected Data – DataSet và SqlDataAdapter « VinhTC

  2. Pingback: [ADO.NET Tutorial] Lesson 05: Làm việc với Disconnected Data – DataSet và SqlDataAdapter | fishgold192

  3. public bool XMLSerialization(string path, DTOVanCo dtovanco)
    {

    // Tạo đối tượng lưu kết quả chuyển đổi
    FileStream fs = new FileStream(path, FileMode.Create); //FileMode.Append: thêm vào | FileMode.Create: ghi đè

    // Tạo đối tượng XmlSerializer
    XmlSerializer xs = new XmlSerializer(typeof(DTOVanCo));

    // Thực thi chuyển đổi
    xs.Serialize(fs, dtovanco);

    fs.Close();

    return true;

    }

    Khi debug chạy đến typeof(DTOVanCo) thì xuất hiện thông báo lỗi “There was an error reflecting type ‘ChinaChessDemo.DTO.DTOVanCo’.”

    Trong class DTOVanCo mình có tạo vài thuộc tính kiểu DTOQuanCo.
    Cho mình hỏi, đoạn code trên bị lỗi gì?

    Phản hồi
    • Như mình nói trong bài: SOAP là một dạng XML đặc biệt dùng trong các giao thức truyền tải mạng. Bạn có thể tìm hiểu các thông tin về SOAP trên google.

      Còn về lỗi này, bạn phải xác nhận field, property nào gây ra lỗi và nguyên nhân thì mới biết được.

      Phản hồi
    • Bạn có thể hiểu Serialize là quá trình chuyển một đối tượng thành một dạng dữ liệu nào đó để có thể truyền, lưu trữ (binary, xml, json),… còn Deserialize là quá trình làm ngược lại.
      Trong câu hỏi bạn dùng từ Serializable là một tính từ có nghĩa là khả năng một đối tượng có thể được serialize hay ko.

      Phản hồi
  4. Pingback: .Net – Serialization và Deserialization trong C# – Mưa bay gió cuốn

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s