序列化 (3) – 自訂序列化行為

  • 1844
  • 0
  • 2013-07-29

在某些特定狀況下,例如變數值的內容在序列化或還原序列化過程中會移失資料時,我們可以運用自訂序列化的行為做處理,強迫序列化分解成為原先的內容。

 

簡介

在某些特定狀況下,例如變數值的內容在序列化或還原序列化過程中會移失資料時,我們可以運用自訂序列化的行為做處理,強迫序列化分解成為原先的內容。

自訂序列化的另外一種常見應用,是針對敏感性的資料物件,例如信用卡卡號,由於直接作序列化可能導致資料外洩,我們可以藉由自訂序列化來改變序列化行為,修改這類資料物料的內容,例如我們可以在序列化過程使用加密技術來改變資料格式,在還原序列化時,以相對應的解密技術將資料還原。

藉由實做 ISerializable 介面來自定物件的序列化行為,必須同時實作 GetObjectData 方法成員和 Constructor 建構式,而 GetObjectData 用以將相關資料填入 Serializarioninfo 物件,定義如下所示:


void GetObjectData(
    SerializationInfo info,
    StreamingContext context
)

 

其中 info 是 SerializationInfo 型別物件參數,用來儲存序列化或還原序列化物件所需的所有資料,而 context 是 StreamingContext 結構型別參數,用來描述指定之序列化資料流的來源和目的端,並提供額外的呼叫端定義內容。

另外,我們必須實作建構式,用於物件還原序列化時將值取出,建構式同樣必須提供 SerializationInfo 型別物件參數和 StreamingContext 結構型別參數。

 

範例說明

修改先前文章的範例,將 ClsSerializable 類別作修改,首先實作 GetObject 方法,此方法當進行子序列化類別物件時會被呼叫,使用 SerializationInfo 類別的 AddValue 方法在物件序列化時儲存替代值。

除了實作 GetObject 方法,我們需要實作特定的建構式,這個建構式包含 SerializationInfo 型別物件參數和 StreamingContext 結構型別參數,建構式中使用 SerializationInfo 所定義 GetValue 方法擷取替代值,並將其重新指派至物件的欄位中。


[Serializable]
public class ClsSerializable : ISerializable
{
    private int _Number = 0;
    private string _Name = "John";
    private string _Cmt = "沒有";
 
    public int Number
    {
        get { return this._Number; }
    }
    public string Name
    {
        get { return this._Name; }
    }
 
    public string Cmt
    {
        get { return this._Cmt; }
    }
 
    public ClsSerializable()
    {
        ModifyMemberValue();
    }
 
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("_Number", _Number);
        info.AddValue("_Name", _Name);
        info.AddValue("_Cmt", _Cmt);
    }
 
    public ClsSerializable(SerializationInfo info, StreamingContext context)
    {
        _Number = (int)info.GetValue("_Number", _Number.GetType());
        _Name = (string)info.GetValue("_Name", _Name.GetType());
        _Cmt = (string)info.GetValue("_Cmt", _Cmt.GetType());
    }
 
    public void ModifyMemberValue()
    {
        this._Number = 7;
        this._Name = "Ou";
        this._Cmt = "喜歡音樂";
    }
}

 

當我們進行序列化時,執行public ClsSerializable() 建構式,呼叫 ModifyMemberValue 方法改變值,接著呼叫 GetObjectData 方法將所要序列化的資料加入物件中,進行序列化動作,我們可以看到序列化後的結果,是變更後的資料值,而非初始值。

 

 

進行還原序列化時,呼叫 public ClsSerializable(SerializationInfo info, StreamingContext context) 建構式,透過 SerializarionInfo 定義的實體方法 GetValue 將 info 成員名稱和型態參數,傳回序列化物件時所指定的成員變數中。

 

完整程式碼

 


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Windows.Forms;
 
namespace WinFormsFormatter
{
    public partial class Form1 : Form
    {
        /// 
        /// 序列化檔案名稱
        /// 
        string FileName = string.Format(@"{0}\{1}", Application.StartupPath, "demo.txt");
 
        public Form1()
        {
            InitializeComponent();
        }
 
        #region 按鈕事件
        private void btnSerialize_Click(object sender, EventArgs e)
        {
            if (rbBinary.Checked == true)
            {
                SerializeBinary();
            }
            else
            {
                SerializeSoap();
            }
        }
 
        private void btnDeserialize_Click(object sender, EventArgs e)
        {
            ClsSerializable clsSerializable = null;
            if (rbBinary.Checked)
            {
                clsSerializable = DeserializeBinary();
            }
            else
            {
                clsSerializable = DeserializeSoap();
            }
 
            // 顯示還原序列化後的類別物件於畫面中
            string strContect = string.Format("Number: {0}\nName: {1}\nCmt: {2}", clsSerializable.Number, clsSerializable.Name, clsSerializable.Cmt);
            this.rtbContent.Text = strContect;
        }
        #endregion
 
        #region BinaryFormatter
        /// 
        /// 使用 BinaryFormatter 進行序列化
        /// 
        private void SerializeBinary()
        {
            // 建立 ClsSerializable 類別物件
            ClsSerializable clsSerializable = new ClsSerializable();
            // 建立檔案資料流物件
            using (FileStream fileStream = new FileStream(FileName, FileMode.Create, FileAccess.Write))
            {
                // 建立 BinaryFormatter 物件
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                // 將物件進行二進位序列化,並且儲存檔案
                binaryFormatter.Serialize(fileStream, clsSerializable);
            }
            // 將序列化後的檔案內容呈現到表單畫面
            StringBuilder sbContent = new StringBuilder();
            foreach (var byteData in File.ReadAllBytes(FileName))
            {
                sbContent.Append(byteData);
                sbContent.Append(" ");
            }
            this.rtbContent.Text = sbContent.ToString();
        }
 
        /// 
        /// 使用 BinaryFormatter 進行還原序列化
        /// 
        /// 
        private ClsSerializable DeserializeBinary()
        {
            // 建立 ClsSerializable 類別物件
            ClsSerializable clsSerializable = null;
            // 建立檔案資料流物件
            using (FileStream fileStream = new FileStream(FileName, FileMode.Open))
            {
                // 建立 BinaryFormatter 物件
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                // 將檔案內容還原序列化成 Object 物件,並且進一步轉型成正確的型別 ClsSerializable
                clsSerializable = (ClsSerializable)binaryFormatter.Deserialize(fileStream);
            }
            return clsSerializable;
        }
        #endregion
 
        #region SoapFormatter
        /// 
        /// 使用 SoapFormatter 進行序列化
        /// 
        private void SerializeSoap()
        {
            // 建立 ClsSerializable 類別物件
            ClsSerializable clsSerializable = new ClsSerializable();
            // 建立檔案資料流物件
            using (FileStream fileStream = new FileStream(FileName, FileMode.Create, FileAccess.Write))
            {
                // 建立 SoapFormatter 物件
                SoapFormatter soapFormatter = new SoapFormatter();
                // 將物件進行 SOAP 序列化,並且儲存檔案
                soapFormatter.Serialize(fileStream, clsSerializable);
            }
            // 將序列化後的檔案內容呈現到表單畫面
            rtbContent.Text = File.ReadAllText(FileName);
        }
 
        /// 
        /// 使用 SoapFormatter 進行還原序列化
        /// 
        /// 
        private ClsSerializable DeserializeSoap()
        {
            // 建立 ClsSerializable 類別物件
            ClsSerializable clsSerializable = null;
            // 建立檔案資料流物件
            using (FileStream fileStream = new FileStream(FileName, FileMode.Open))
            {
                // 建立 SoapFormatter 物件
                SoapFormatter soapFormatter = new SoapFormatter();
                // 將檔案內容還原序列化成 Object 物件,並且進一步轉型成正確的型別 ClsSerializable
                clsSerializable = (ClsSerializable)soapFormatter.Deserialize(fileStream);
            }
            return clsSerializable;
        }
        #endregion
 
        [Serializable]
        public class ClsSerializable : ISerializable
        {
            private int _Number = 0;
            private string _Name = "John";
            private string _Cmt = "沒有";
 
            public int Number
            {
                get { return this._Number; }
            }
            public string Name
            {
                get { return this._Name; }
            }
 
            public string Cmt
            {
                get { return this._Cmt; }
            }
 
            public ClsSerializable()
            {
                ModifyMemberValue();
            }
 
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("_Number", _Number);
                info.AddValue("_Name", _Name);
                info.AddValue("_Cmt", _Cmt);
            }
 
            public ClsSerializable(SerializationInfo info, StreamingContext context)
            {
                _Number = (int)info.GetValue("_Number", _Number.GetType());
                _Name = (string)info.GetValue("_Name", _Name.GetType());
                _Cmt = (string)info.GetValue("_Cmt", _Cmt.GetType());
            }
 
            public void ModifyMemberValue()
            {
                this._Number = 7;
                this._Name = "Ou";
                this._Cmt = "喜歡音樂";
            }
        }
    }
}


 

結語

本文說明了如何自定序列化行為,透過實作 GetObjectData 方法和 Construct 建構式來達成。本系列文章針對序列化基本技術做說明,並且以實際例子解說,希望能對您有幫助。

 

其他相關資訊

SerializationInfo 類別

StreamingContext 結構

ISerializable.GetObjectData 方法