當我們使用 BinaryFormatter 或 XmlSerializer 去序列化一個類別時,系統會依設定幫我們直接產生序列化後的資訊。 但是,如果遇到版本的問題,或者某些類別不支援序列化時,導至還原序列化後的資訊不符合需求, 這時候就是使用自訂序列化的時候,也就是覆寫.NET Framework內建的序列化程式,並自行撰寫程式碼以控制序列化後的資訊。 使用自訂序列化,可以在不破壞該類別的前提下,達到上述問題的解決。
如何實作自訂序列化
若要自訂序列化,就要實作 ISerializable 介面,並且將 Serializable 屬性套用到該類別即可。
要實作 ISerializable 介面,就必須實作以下二件事:
- 實作 GetObjectData 方法,在這方法中填入序列化所需的資訊到 SerializationInfo 物件之中。
- 建立用來還原序列化時的特殊建構函式。
這個方法和建構函式的參數都是 SerializationInfo 和 StreamingContext 型別。
在 GetObjectData 方法之中,主要必須撰寫的程式碼,就是使用 AddValue 方法,將想要序列化的變數,以 name/value 方式加入到 SerializationInfo 物件之中。
使 SerializationInfo 這個物件具有足夠的序列化資訊,以便在還原序列化過程中用來重建該物件。
當執行階段呼叫你的還原序列化建構函式時,它會使用在序列化過程中使用的變數名稱從 SerializationInfo 取該變數的值。
//宣告成可序列化
[Serializable]
public class Grade : ISerializable
{
public string name;
public int math;
public int english;
[OptionalField] public int chinese;
[NonSerialized] public int total;
// The standard, non-serialization constructor
public Grade(string _name, int _math, int _english, int _chinese)
{
name = _name;
math = _math;
english = _english;
chinese = _chinese;
total = _math + _english + _chinese;
}
// 具有 SerializationInfo 和 StreamingContext 型別參數的特殊 constructor
// 這個 constructor 在還原序列化時會使用到
protected Grade(SerializationInfo info, StreamingContext context)
{
name = info.GetString("Name");
math = info.GetInt32("MathGrade");
english = info.GetInt32("EnglishGrade");
chinese = info.GetInt32("ChineseGrade");
total = math + english + chinese;
}
// 實做 GetObjectData 方法,此方法會在叫用 Serialize 方法時自動被呼叫
// GetObjectData 的參數同 constructor
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", name);
info.AddValue("MathGrade", english);
info.AddValue("EnglishGrade", chinese);
info.AddValue("ChineseGrade", total);
}
[OnSerializing] //截取 OnSerializing
void CalculateTotal(StreamingContext sc)
{
total = math + english + chinese;
}
[OnDeserialized] //截取 OnDeserialized
void CheckTotal(StreamingContext sc)
{
if (total == 0) { CalculateTotal(sc); }
}
}
執行自訂序列化與自訂還原序列化。
//Custom Serialize (Using SoapFormatter)
private void btnSerialize_Click(object sender, EventArgs e)
{
FileStream filestream = new FileStream("Custom_Class.SOAP", FileMode.Create); //建立FileStream
SoapFormatter formatter = new SoapFormatter(); //建立BinaryFormatter
Grade grade = new Grade("vito", 80, 90, 30);
formatter.Serialize(filestream, grade); //透過 SoapFormatter 序列化物件
filestream.Close();
}
//Custom Deserialize (Using SoapFormatter)
private void btnDeserialize_Click(object sender, EventArgs e)
{
FileStream filestream = new FileStream("Custom_Class.SOAP", FileMode.Open);
SoapFormatter formatter = new SoapFormatter(); //建立SoapFormatter
Grade grade = (Grade)formatter.Deserialize(filestream);
filestream.Close();
}
回應序列化事件
序列化或還原序列化過程中,會引發下列四種事件。只要將這些屬性套用到回應的方法上,即可在過程中變更物件資訊。
- OnSerializing
- OnSerialized
- OnDeserializing
- OnDeserialized
回應這些事件的方法,必須符合下列要求:
- 在方法上,套用上面想要截取的事件屬性。
- 可接受一個 StreamingContext 物件為參數。
- 回傳 void
XmlDocument
XmlDocument doc = new XmlDocument();
doc.Load("Log.xml");
//取得文件的根節點
XmlNode root = doc.DocumentElement;
//建立新的節點
XmlElement elem = doc.CreateElement("Log");
elem.SetAttribute("User", "vio"); //設定節點 屬性
elem.InnerText = DateTime.Now.ToString(); //設定節點 InnerText
//附加節點
root.AppendChild(elem);
//儲存文件
doc.Save("Log.xml");
<?xml version="1.0" encoding="utf-8"?> <Logs> <Log User="aaa">2012/2/13 上午 10:08:00</Log> <Log User="bbb">2012/2/14 上午 11:08:00</Log> <Log User="vio">2012/9/4 上午 10:32:36</Log> </Logs>
沒有留言:
張貼留言