[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;
}
}
}
序列化文件
輸出結果
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;
}
}
}
}
輸出文件
輸出結果
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;
}
}
}
}
輸出文件
輸出結果
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 = "這是修正過的字串 ; ";
}
}
輸出文件
輸出結果
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();
}
}
}
輸出文件
輸出結果
Reference
IDeserializationCallback..::.OnDeserialization 方法
三小俠 小弟獻醜,歡迎指教