在某些特定狀況下,例如變數值的內容在序列化或還原序列化過程中會移失資料時,我們可以運用自訂序列化的行為做處理,強迫序列化分解成為原先的內容。
簡介
在某些特定狀況下,例如變數值的內容在序列化或還原序列化過程中會移失資料時,我們可以運用自訂序列化的行為做處理,強迫序列化分解成為原先的內容。
自訂序列化的另外一種常見應用,是針對敏感性的資料物件,例如信用卡卡號,由於直接作序列化可能導致資料外洩,我們可以藉由自訂序列化來改變序列化行為,修改這類資料物料的內容,例如我們可以在序列化過程使用加密技術來改變資料格式,在還原序列化時,以相對應的解密技術將資料還原。
藉由實做 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 建構式來達成。本系列文章針對序列化基本技術做說明,並且以實際例子解說,希望能對您有幫助。