[C#] 序列化 (Serialization)

  • 64312
  • 0
  • 2010-06-02

[C#] 序列化 (Serialization)

Introduction

當物件需要封送、遠端服務甚至是網路資料流都運用了序列化的技術,或是要將物件直接儲存到資料庫裡,也都需要序列化的技術。

.net 提供了幾個類別來幫我們完成序列化的行為。

  • BinaryFormatter :以二進位格式序列化和還原序列化物件或整個連接物件的 Graph。
  • SoapFormatter:以 SOAP 格式序列化和還原序列化物件或整個連接物件 Graph。
  • XmlSerializer:將物件序列化成為 XML 文件,以及從 XML 文件將物件還原序列化。XmlSerializer 可讓您控制如何將物件編碼為 XML。

 

Example

sample1

二進位格式序列化及反序列化


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

namespace TestBinarySerialization {    
    //建立可序列化類別
    [Serializable]
    public class ClsSerializable {
        private int _Number;
        private string _Demo;
        public ClsSerializable() {
            this._Number = 254;
            this._Demo = "this is a book";
        }

        public int Number {
            get { return this._Number; }
        }
        public string Demo {
            get { return this._Demo; }
        }
    }

    class Program {        
        static void Main(string[] args) {
            Program oProgram = new Program();
            oProgram.SerializeBinary();
            ClsSerializable  o = oProgram.DeSerialize();
            Console.WriteLine("============驗證還原序列化物件===============");
            Console.WriteLine("ClsSerializable.Number : " + o.Number);
            Console.WriteLine("ClsSerializable.Demo : " + o.Demo);
            Console.ReadKey();
        }

        //序列化函式
        private void SerializeBinary() {
            //建立物件
            ClsSerializable oClsSerializable = new ClsSerializable();
            //建立資料流物件
            FileStream oFileStream = new FileStream(@"C:\sbinary.txt", FileMode.Create);
            //建立二進位格式化物件
            BinaryFormatter myBinaryFormatter = new BinaryFormatter();
            Console.WriteLine("二進位格式序列化......");
            //將物件進行二進位格式序列化,並且將之儲存成檔案
            myBinaryFormatter.Serialize(oFileStream, oClsSerializable);
            oFileStream.Flush();
            oFileStream.Close();
            oFileStream.Dispose();
            Console.WriteLine("完成進位格式序列化......");
        }

        //反序列函式
        private ClsSerializable DeSerialize() {
            ClsSerializable o = null;
            FileStream oFileStream = new FileStream(@"C:\sbinary.txt", FileMode.Open);
            BinaryFormatter myBinaryFormatter = new BinaryFormatter();
            Console.WriteLine("開始還原序列化物件......");
            //將檔案還原成原來的物件
            o = (ClsSerializable)myBinaryFormatter.Deserialize(oFileStream);
            return o;
        }
    }
}

序列化文件

2009-12-15_225550

輸出結果

2009-12-15_225723

 

 

sample2

SOAP 格式序列化及反序列化


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

namespace TestSOAPSerializtion {
    //建立可序列化類別
    [Serializable]
    public class ClsSerializable {
        private int _Number;
        internal string _Demo;
        public string Company = "C#";

        public ClsSerializable() {
            this._Number = 254;
            this._Demo = "this is a book";
        }

        public int Number {
            get { return this._Number; }
        }
        public string Demo {
            get { return this._Demo; }
        }
    }

    class Program {
        static void Main(string[] args) {
            Program oProgram = new Program();
            //序列化物件
            oProgram.SOAPSerializeSoap();
            ClsSerializable oClsSerializable = null;
            //反序列化物件
            oClsSerializable = oProgram.SOAPDeSerialize();
            Console.WriteLine("oClsSerializable._Demo : " + oClsSerializable._Demo);
            Console.WriteLine("oClsSerializable.Company : " + oClsSerializable.Company);
            Console.WriteLine("oClsSerializable.Number : " + oClsSerializable.Number);

            Console.ReadKey();
        }

        private void SOAPSerializeSoap() {
            //建立要備序列化的物件
            ClsSerializable oClsSerializable = new ClsSerializable();
            //建立資料流物件,並將資料存成 .dat 檔
            using (FileStream oFileStream = new FileStream(@"C:\soap.dat", FileMode.Create)) {
                Console.WriteLine("開始進行 SOAP 格式序列化.......");
                //建立 SoapFormatter 物件
                SoapFormatter oSoapFormatter = new SoapFormatter();
                //序利化物件
                oSoapFormatter.Serialize(oFileStream, oClsSerializable);

                Console.WriteLine("序列化完成,資料儲存於文字文件 soap.dat .......");
            }
        }

        private ClsSerializable SOAPDeSerialize() {
            //建立資料流物件
            using (FileStream oFileStream = new FileStream(@"C:\soap.dat", FileMode.Open)) {
                Console.WriteLine("開始進行 SOAP 格式反序列化.......");
                //建立 SoapFormatter 物件
                SoapFormatter oSoapFormatter = new SoapFormatter();
                //反序利化物件
                ClsSerializable o = (ClsSerializable)oSoapFormatter.Deserialize(oFileStream);
                Console.WriteLine("物件完成還原.......");
                return o;
            }
        }
    }
}

輸出文件

2009-12-15_232346

輸出結果

2009-12-15_232836

 

 

 

 

sample3

Xml 序列化及反序列化


using System.IO;
using System.Xml.Serialization;

namespace TestXMLSerialization {
    //建立可序列化類別
    [Serializable]
    public class ClsSerializable {
        private int _Number;
        internal string _Demo;
        public string Company = "C#";

        public ClsSerializable() {
            this._Number = 254;
            this._Demo = "this is a book";
        }

        public int Number {
            get { return this._Number; }
        }
        public string Demo {
            get { return this._Demo; }
        }
    }

    public class Program {
        static void Main(string[] args) {
            Program oProgram = new Program();
            //序列化物件
            oProgram.SerializeXML();
            //取得反序列化物件            
            ClsSerializable o = oProgram.DeSerializeXML();
            Console.WriteLine("==========================================");
            Console.WriteLine("ClsSerializable.Number : " + o.Number);
            Console.WriteLine("ClsSerializable.Number : " + o.Demo);
            Console.ReadKey();
        }

        //序列化物件XML
        private void SerializeXML() {            
            ClsSerializable oClsSerializable = new ClsSerializable();
            using (FileStream oFileStream = new FileStream(@"C:\MyXML.xml", FileMode.Create)) {
                Console.WriteLine("準備序列化物件");
                XmlSerializer oXmlSerializer = new XmlSerializer(typeof(ClsSerializable));
                oXmlSerializer.Serialize(oFileStream, oClsSerializable);
                oFileStream.Close();
                Console.WriteLine("序列化物件完成");

            }
        }
        //反序列化
        private ClsSerializable DeSerializeXML() {
            using (FileStream oFileStream = new FileStream(@"C:\MyXNL.xml", FileMode.Open)) {
                ClsSerializable o = null;
                Console.WriteLine("準備還原序列化物件");
                XmlSerializer oXmlSerializer = new XmlSerializer(typeof(ClsSerializable));
                o = (ClsSerializable)oXmlSerializer.Deserialize(oFileStream);
                oFileStream.Close();
                Console.WriteLine("還原完成");
                return o;
            }
        }
    }
}

輸出文件

2009-12-16_080213

輸出結果

2009-12-16_080512

 

sample4

自訂序列化行為

實做 ISerializable 介面,允許物件控制它自己的序列化 (Serialization) 和還原序列化 (Deserialization)。

任何實做這個介面的類別,必須同時實做方法 GetObjectData() 以及 Constructor 建構式;


void GetObjectData(
    SerializationInfo info,
    StreamingContext context
)

GetObjectData 用以將物件相關資訊填入 SerializationInfo 物件,其中 info 為一 SerializationInfo 型別的物件參數,SerializationInfo 是一種設計用以儲存

物件自訂序列化操作時所需的資訊,context 是一種 StreamingContext 結構型別參數,表示序列化操作的來源資料流。

除了實做 GetobjectData 方法,自訂序列化的過程,還必須完成特定建構式的實作,建構式在物件還原序列化時,將上述 Info() 參數內容傳送至類別。

 


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

class UsingISerializable {
    static void Main(string[] args) {
        ClsISerialization myClsIS = new ClsISerialization();
        //序列化,將資料序列化寫入檔案
        FileStream myFileStream = new FileStream(@"c:\ttt.bin", FileMode.Create);
        //建立 BinaryFormatter 物件
        BinaryFormatter myBinaryFormatter = new BinaryFormatter();

        myBinaryFormatter.Serialize(myFileStream, myClsIS);
        myClsIS = null;
        myFileStream.Close();
        // 反序列化,從檔案取出資料成員           
        myFileStream = new FileStream(@"c:\ttt.bin", FileMode.Open);
        myBinaryFormatter = new BinaryFormatter();
        myClsIS = (ClsISerialization)
        myBinaryFormatter.Deserialize(myFileStream);
        myFileStream.Close();
        // 於控制臺輸出資料			
        Console.WriteLine(myClsIS.message);
        Console.WriteLine(myClsIS.intNumber);
        Console.WriteLine(myClsIS.lngTest);

        for (int i = 0; i < 10; i++) {
            Console.WriteLine(myClsIS.intArrayX[i]);
        }
        Console.ReadKey();
    }
}

[Serializable]
public class ClsISerialization : ISerializable {
    public int intNumber = 1000;
    public string message = "這是測試字串 !!";
    public long lngTest;
    public int[] intArrayX = new int[10];
    public int[] intArrayY = new int[10];

    public ClsISerialization() {
        ChangeMmberValue();
    }

    //實做方法,此方法,會在類別實體化後,自動被呼叫
    public void GetObjectData(SerializationInfo info, StreamingContext context) {
        info.AddValue("intNumber", intNumber);
        info.AddValue("message", message);
        info.AddValue("intArrayX", intArrayX);
    }

    //建構式,用於還原序列化物件
    public ClsISerialization(SerializationInfo info, StreamingContext context) {
        intNumber = (int)info.GetValue("intNumber", typeof(int));
        message = (string)info.GetValue("message", typeof(string));
        intArrayX = (int[])info.GetValue("intArrayX", typeof(int[]));
    }


    void ChangeMmberValue() {
        for (int i = 0; i < 10; i++) {
            intArrayX[i] = i * 100;
        }
        intNumber = 2000;
        message = "這是修正過的字串 ;   ";
    }
}

輸出文件

2009-12-16_084327

輸出結果

2009-12-16_084252

 

sample5

修正無法序列化的資料

序列化物件時,我們可以使用屬性 [NonSerialized],將物件中的某些成員標示為不可序列化的,所以當你還原物件的時候,

被標示為 [NonSerialized] 的成員,還原時,將失去原本的值。如下


public int intNumber = 254;
public string strDemo = "This is a test string";
[NonSerialized]
public long lngNumber = 123456;

上式 lngnumber 被標示 [NonSerialized],在序列化的時候沒有辦法被分解,因此,當重組物件時,輸出的結果, lngNumber = 0 而

不是原先的 123456 。

 

若要改變上述這種預設的行為,可以在設計類別的時候,繼承 IDeserializationCallback 介面的類別物件,在還原序列化的時候,

會先呼叫 OnDeserialization() 方法,當物件含有大量的暫時性資料,此時當你序列化物件的時候,會同時分解這些暫時性的資料,

這些資料會被一併寫入檔案,這是沒有意義的;那該怎麼還原未被序列化分解的成員變數。


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

namespace TestIDeserializationCallback {
    [Serializable()]
    class SerializeObject : IDeserializationCallback {
        public int firstNumber;
        public int secondNumber;

        [NonSerialized]
        public int thirdNumber;
        [NonSerialized]
        public int forthNumber;

        //建構式
        public SerializeObject() {
            //初始化成員
            firstNumber = 1000;
            secondNumber = 2000;
            thirdNumber = 100;
            forthNumber = 200;
        }
        //實做方法,當物件還原時,會自動呼叫
        public void OnDeserialization(Object sender) {
            //還原 thirdNumber 成員
            thirdNumber = 100;
        }
    }

    class Program {
        static void Main(string[] args) {
            SerializeObject mySerializeObject = new SerializeObject();
            FileStream myFileStream =
                new FileStream(@"c:\AAA.bin", FileMode.Create);
            BinaryFormatter myBinaryFormatter = new BinaryFormatter();

            Console.WriteLine("開始序列化物件 mySerializeObject  ....");
            myBinaryFormatter.Serialize(myFileStream, mySerializeObject);
            Console.WriteLine("開始重組物件 mySerializeObject ....");
            myFileStream.Close();

            // 反序列化,從檔案取出資料成員           
            myFileStream = new FileStream(@"c:\ttt.bin", FileMode.Open); ;
            mySerializeObject = (SerializeObject)
    myBinaryFormatter.Deserialize(myFileStream);

            Console.WriteLine("輸出物件內容 ....");
            Console.WriteLine
    ("firstNumber 變數值等於 {0}"
    , mySerializeObject.firstNumber);
            Console.WriteLine
    ("secondNumber 變數值等於 {0} "
    , mySerializeObject.secondNumber);
            Console.WriteLine
    ("thirdNumber 變數值等於 {0} "
    , mySerializeObject.thirdNumber);
            Console.WriteLine
    ("forthNumber 變數值等於 {0} "
    , mySerializeObject.forthNumber);

            myFileStream.Close();
            Console.ReadKey();
        }
    }
}

輸出文件

2009-12-16_200546

輸出結果

2009-12-16_200627

 

 

Reference

序列化

BinaryFormatter 類別

SoapFormatter 類別

XmlSerializer 類別

ISerializable 介面

IDeserializationCallback 介面

IDeserializationCallback..::.OnDeserialization 方法

XML 序列化的範例

三小俠  小弟獻醜,歡迎指教