.net對象序列化解析

  一.二進制格式器(Binary Formatter) vs XML格式器(XML Formatter):

  下面我先向你們介紹兩種不一樣的格式器,分別用它們如何實現序列化機制和反序列化機制,請看下面的代碼:程序員

#region Binary Serializers
public static System.IO.MemoryStream SerializeBinary(object request) {
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = 
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}網絡

public static object DeSerializeBinary(System.IO.MemoryStream memStream) {
 memStream.Position=0;
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = 
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 object newobj = deserializer.Deserialize(memStream);
 memStream.Close();
 return newobj;
}
#endregion數據結構

#region XML Serializers
public static System.IO.MemoryStream SerializeSOAP(object request) {
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer = 
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}併發

public static object DeSerializeSOAP(System.IO.MemoryStream memStream) {
 object sr;
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer = 
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 memStream.Position=0;
 sr = deserializer.Deserialize(memStream);
 memStream.Close();
 return sr;
}
#endregion框架

  從上面的代碼咱們能夠發現不管運用哪一種格式器,其基本的過程都是同樣的,並且都是很是容易實現的,惟一的不一樣就是定義格式器的類型不一樣。不過在實際的應用中,二進制格式器每每應用於通常的桌面程序和網絡通信程序中,而XML格式器稟承了XML技術的優勢,大多數被應用於.Net Remoting和XML Web服務等領域。下面咱們來分析一下兩種格式器各自的優勢。

  二進制序列化的優勢:

  1. 全部的類成員(包括只讀的)均可以被序列化;

  2. 性能很是好。

  XML序列化的優勢:

  1. 互操做性好;

  2. 不須要嚴格的二進制依賴;

  3. 可讀性強。

  經過分析上面的代碼,咱們知道了選擇二進制序列化的方式仍是選擇XML序列化的方式僅僅是對不一樣的格式器進行選擇而已。你能夠根據實際的須要選擇相應的格式器完成序列化和反序列化工做。同時請注意,代碼中的序列化函數和反序列化函數僅僅是在調用Serialize()和Deserialize()這兩個核心函數上產生了差異,即它們的參數不一樣。所以以上的代碼完成了一些最最基本可是很重要的功能,你能夠將它們運用在你的程序中,或是將其進行適當擴充以知足程序的特定須要。函數

  二.序列化機制對類的要求:

  若是你要對一個對象進行序列化,那麼你必須將它的類型標記爲[Serializable()],該操做是經過SerializableAttribute屬性來實現的。將SerializableAttribute屬性應用於一種數據類型可代表該數據類型的實例能夠被序列化。若是正在序列化的對象圖中的任何類型未應用SerializableAttribute屬性,公共語言運行庫則會引起SerializationException。默認狀況下,類型中由SerializableAttribute標記的全部公共和私有字段都會進行序列化,除非該類型實現ISerializable接口來重寫序列化進程(經過實現該接口咱們即可以實現將在後面介紹的"自定義序列化")。默認的序列化進程會排除用NonSerializedAttribute屬性標記的字段,即你能夠將該類型標記爲[NonSerialized()]以代表它是不能夠被序列化的。若是可序列化類型的字段包含指針、句柄或其餘某些針對於特定環境的數據結構,而且不能在不一樣的環境中以有意義的方式重建,則最好將NonSerializedAttribute屬性應用於該字段。有關序列化的更多信息,請參閱System.Runtime.Serialization名字空間中的相關內容。

  下面我給你們介紹一個例子,以顯示如何正確的運用SerializableAttribute屬性和NonSerializedAttribute屬性。該程序中運用到了XML格式器,不過同時給出了二進制格式器爲參考(程序中將其用"//"標註),其實現的結果是同樣的。該程序實現的功能是在序列化和反序列化操做先後測試對象因包含了[NonSerialized()]的字段而顯示不一樣的屏幕打印結果。其代碼以下:性能

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
//using System.Runtime.Serialization.Formatters.Binary;測試

public class Test {
 public static void Main() {
  // 建立一個新的測試對象
  TestSimpleObject obj = new TestSimpleObject();this

  Console.WriteLine("Before serialization the object contains: ");
  obj.Print();spa

  // 建立一個文件"data.xml"並將對象序列化後存儲在其中
  Stream stream = File.Open("data.xml", FileMode.Create);
  SoapFormatter formatter = new SoapFormatter();
  //BinaryFormatter formatter = new BinaryFormatter();

  formatter.Serialize(stream, obj);
  stream.Close();
  
  // 將對象置空
  obj = null;

  // 打開文件"data.xml"並進行反序列化獲得對象
  stream = File.Open("data.xml", FileMode.Open);
  formatter = new SoapFormatter();
  //formatter = new BinaryFormatter();

  obj = (TestSimpleObject)formatter.Deserialize(stream);
  stream.Close();

  Console.WriteLine("");
  Console.WriteLine("After deserialization the object contains: ");
  obj.Print();
 }
}

// 一個要被序列化的測試對象的類
[Serializable()] 
 public class TestSimpleObject {
 public int member1;
 public string member2;
 public string member3;
 public double member4;

 // 標記該字段爲不可被序列化的
[NonSerialized()] public string member5; 

public TestSimpleObject() {
 member1 = 11;
 member2 = "hello";
 member3 = "hello";
 member4 = 3.14159265;
 member5 = "hello world!";
}

public void Print() {
 Console.WriteLine("member1 = '{0}'", member1);
 Console.WriteLine("member2 = '{0}'", member2);
 Console.WriteLine("member3 = '{0}'", member3);
 Console.WriteLine("member4 = '{0}'", member4);
 Console.WriteLine("member5 = '{0}'", member5);
}
}

  三.基本序列化(Basic Serialization) vs 自定義序列化(Custom Serialization):

  .Net框架爲咱們提供了兩種方式的序列化:一種爲基本序列化、另外一種爲自定義序列化。值得注意的是,序列化的方式和前面提到的序列化的格式是不一樣的概念。序列化的方式是指.Net框架將程序的數據轉化爲能被存儲並傳輸的格式的實際過程,它是無論程序員運用了何種類型的格式器的(二進制格式器仍是XML格式器)。而序列化的格式則指程序的數據是被轉化成二進制格式了仍是被轉化成XML格式了。

  完成序列化的最簡單的方法即是讓.Net框架自動爲咱們完成整個過程,而咱們沒必要去管它內部是如何具體實現的,這種方法即是前面提到的"基本序列化"。在這種方式下,咱們須要作的僅僅是將類標記上[Serializable()]屬性。而後.Net框架便調用該類的對象並將它轉化爲所需的格式。同時你還能夠控制其中的某些字段不被序列化,方法就是前面所述的將該字段標記上[NonSerialized()]屬性。這樣,最最簡單和基本的序列化工做就完成了,不過其內部是如何實現的你是不得而知的,同時你也不能進一步控制序列化過程的程序行爲。

  若是你要得到對序列化的更大的控制權,那麼你就得使用"自定義序列化"的方式。經過使用這種方式,你能夠徹底的控制類的哪些部分能被序列化而哪些部分不能,同時你還能夠控制如何具體的進行序列化。運用該方式的好處就是能克服基本序列化所會遇到的問題。咱們在運用基本序列化將一個類的對象序列化完畢並存儲在文件中後,假設該對象原來有三個字段,若是此時該對象增長了一個字段,那麼再將該對象從文件中反序列化出來時會發生字段數不一致的錯誤。這樣的問題是基本序列化所不能解決的,只能運用自定義序列化的方式來解決。

  在介紹自定義序列化以前,我先給出介紹過程當中所要用到的實例程序的代碼。這是一個時間安排程序,其中要用到將不一樣的時間格式進行轉化的操做。因此運用序列化的機制能很好的解決這個問題。

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class Schedule {
  protected System.DateTime start;
  protected System.DateTime end;
  // 每一個時間間隔所要增長的毫秒數
  protected long interval;

  public System.DateTime Start {get{return start;}set{start=value;}}
  public System.DateTime End {get{return end;}set{end=value;}}
  public long Interval {get{return interval;}set{interval=value;}}
  public Schedule(System.DateTime Start, System.DateTime End, long Interval) {
   start=Start;
   end=End;
  interval=Interval;
}

// 若是已經到告終束的時間,則返回結束時間,不然返回下一次運行的時間
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 }
}
}
}

  自定義序列化:

  下面我就向你們介紹自定義序列化以及反序列化的具體過程。首先,程序的類必須實現System.Runtime.Serialization.ISerializable接口,該接口的功能就是容許對象控制其本身的序列化和反序列化過程。因此咱們得從新定義上面的類:

[Serializable()]
public class ScheduleCustom : System.Runtime.Serialization.Iserializable

  接下來,咱們必須對該接口調用GetObjectData()的實現,也即咱們必須在上面的類中給出GetObjectData()的具體實現。其函數原型以下:

void GetObjectData(SerializationInfo info, StreamingContext context);

  上面的類中GetObjectData()的具體實現以下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 運用info對象來添加你所須要序列化的項
info.AddValue("start", start);
info.AddValue("end", end);
info.AddValue("interval", interval);
}

   然而對於這麼一個簡單的方法,讀者可能不能理會到系列化帶來的強大功能,因此下面我就給這個方法添加一些東西。若是在系列化過程當中咱們要查看類型爲DateTime的"start"屬性的輸出的話,其結果會是.Net框架默認的格式:

  2002-12-19T14:09:13.3457440-07:00

  而對於沒有.Net框架的用戶,或是在其餘時間區域內的用戶而言,這麼一個格式的時間多是很是難以理解的,因此咱們有必要將時間的格式轉化爲格林威治標準時間格式,因而修改GetObjectData()方法以下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 運用info對象來添加你所須要序列化的項
// 同時,將"start"和"end"屬性的時間格式轉化爲格林威治標準時間格式
info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
info.AddValue("interval", interval);
info.AddValue("timeformat", "utc");
}

  這樣一來,咱們在系列化過程當中查看"start"屬性時就會獲得以下結果:

   8/19/2002 9:09:13 PM

  同時請注意咱們在GetObjectData()方法中添加的一個名爲"timeformat"的額外屬性,經過它咱們能夠方便的知道系列化過程當中所使用的時間格式。若是有興趣的話,你還能夠從System.Globalization.DateTimeFormatInfo這個名字空間中獲取更多有關時間格式的信息。

  自定義反序列化:

  你能夠經過調用一個自定義的構造函數來完成自定義反序列化的操做。該構造函數的定義以下:

public ScheduleCustom (SerializationInfo info,StreamingContext context);

  在上面的類中,咱們的ScheduleCustom()方法將完成把時間格式從格林威治標準時間格式反序列化爲當地時間的格式的操做,其函數實現以下:

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
}

  在完成自定義序列化和自定義反序列化後,咱們的時間安排程序變成了以下形式:

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class ScheduleCustom : System.Runtime.Serialization.ISerializable {
 protected System.DateTime start;
 protected System.DateTime end;
 // 每一個時間間隔所要增長的毫秒數
 protected long interval;

 public System.DateTime Start {get{return start;}set{start=value;}}
 public System.DateTime End {get{return end;}set{end=value;}}
 public long Interval {get{return interval;}set{interval=value;}}

 public ScheduleCustom(System.DateTime Start, System.DateTime End, long Interval) {
  start=Start;
  end=End;
  interval=Interval;
 }

// 若是已經到告終束的時間,則返回結束時間,不然返回下一次運行的時間
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 } 
}

public void GetObjectData(SerializationInfo info,StreamingContext context) {
 // 運用info對象來添加你所須要序列化的項
 // 同時,將"start"和"end"屬性的時間格式轉化爲格林威治標準時間格式
 info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
 info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
 info.AddValue("interval", interval);
 info.AddValue("timeformat", "utc");
}

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
 }
}
}

  四.總結:  本文向你們介紹了.Net框架下系列化機制的一些基本概念和基本的運用方法,讀者在讀完本文後,應該對如下幾個概念有個初步瞭解:二進制系列化、XML系列化、基本序列化和自定義系列化,並應可以完成一些基本的系列化應用。最後,但願你們能合理有效的運用系列化機制併發揮它的功效以更好地知足實際工做須要。

相關文章
相關標籤/搜索