.NET中的序列化

 

序列化定義          序列化是將對象狀態轉換爲可保持或傳輸的格式的過程。與序列化相對的是反序列化,它將流轉換爲對象。在此過程中,先將對象的公共字段和私有字段以及類的名稱(包括類所在的程序集)轉換爲字節流,然後再把字節流寫入數據流。在隨後對對象進行反序列化時,將創建出與原對象完全相同的副本。

序列化的目的 1、以某種存儲形式使自定義對象持久化; 2、將對象從一個地方傳遞到另一個地方。
3、對象封送,遠程服務甚至網絡數據流都運用了序列化的技術。
序列化運行機制          實質上序列化機制是將類的值轉化爲一個一般的(即連續的)字節流,然後就可以將該流寫到磁盤文件或任何其他流化目標上。將對象的狀態信息轉換爲可以存儲或傳輸的窗體的過程。在序列化期間,對象將其當前狀態寫入到臨時或持久性存儲區。以後,可以通過從存儲區中讀取或反序列化對象的狀態,重新創建該對象。序列化使其他代碼可以查看或修改那些不序列化便無法訪問的對象實例數據。確切地說,代碼執行序列化需要特殊的權限:即指定了 SerializationFormatter 標誌的 SecurityPermission。在默認策略下,通過 Internet 下載的代碼或 Intranet 代碼不會授予該權限;只有本地計算機上的代碼才被授予該權限。通常,對象實例的所有字段都會被序列化,這意味着數據會被表示爲實例的序列化數據。這樣,能夠解釋該格式的代碼有可能能夠確定這些數據的值,而不依賴於該成員的可訪問性。類似地,反序列化從序列化的表示形式中提取數據,並直接設置對象狀態,這也與可訪問性規則無關。對於任何可能包含重要的安全性數據的對象,如果可能,應該使該對象不可序列化。如果它必須爲可序列化的,請嘗試生成特定字段來保存不可序列化的重要數據。如果無法實現這一點,則應注意該數據會被公開給任何擁有序列化權限的代碼,並確保不讓任何惡意代碼獲得該權限。
序列化的應用          1、在用戶登錄後,對界面作一些個性化設置(如:背景色,佈局,字體等),爲了使用用戶關閉網頁後能夠保留設置,以便在下次登錄時再加載上次的設置,我們可以將用戶的設置信息保存在一個對象中,然後把該對象序列化保存在表的某個字段中,在加載網頁的時候取出字段中的信息,並反序列化生成設置對象,應用到用戶界面上。          2、對用戶的一些不用作查詢的信息(如:住址,Email,家庭成員,工作經歷等)序列化後保存在一個表的字段中,在需求發生變化時(如增加用戶新的信息),不用動態增加字段。在需要使用的時候,取出字段中的信息反序列化成對象就可以了。          3、在點對點兩人聊天系統中,一個用戶輸入的內容(如:彩色文字,圖片等)後顯示輸入時的內容和樣式,另一個用戶界面中也應當顯示同樣的內容和樣式。這時我們可以把用戶輸入的內容(如:彩色文字,圖片等)封裝爲一個對象,然後序列化到二進制網絡流中去,在另一端,取出二進制流並反序列化成對象,然後顯示在界面上。

序列化種類          .NET框架提供兩種格式的序列化,二進制序列化(Binary Serialization)和XML序列化(XML Serialization)。

         1. 二進制序列化:將對象分解成爲簡單的二進制格式,這種類型的序列化並不會遺失原來的類型數據,對象使用二進制序列化分解之後,可以被傳送至各種存儲媒介,包括數據流,磁盤,內存甚至網絡上連接的主機。          2. XML序列化:將對象分解成爲XML格式,使用SOAP標準來訪問,由於XML與SOAP是開放標準,因此特別適用於對象需要跨越網絡傳送的情形,與二進制序列化不同的是,使用這種格式的序列化,只會序列化分解類型中的公用(Public)屬性和字段等內容。

序列化類的功能          .NET框架針對兩種不同格式的序列化技術,均提供了相應的類:BinaryFormatter類以及SoapFormatter ,XmlSerializer類。

BinaryFormatter用於二進制格式序列化的操作,位於如下命名空間中: System.Runtime.Serialization.Formatters.Binary

SoapFormatter提供XML序列化的支持,其所處的命名空間如下: System.Runtime.Serialization.Formatters.Soap

XmlSerializer提供XML序列化支持,所處命名空間爲: System.Xml.Serialization

        這三種類提供對象序列化分解操作以及還原序列化的相關方法,簡化序列化對象所需的實現細節。而要想實際的寫出這個流,就要使用那些實現了IFormatter接口的類裏的Serialize和Deserialize方法。

1.BinaryFormatter類用來將對象進行二進制的格式化分解,並將二進制格式化分解的對象組合還原,Serialize() 以及Deserialize() 方法分別用來序列化以及還原序列化。下面是Serialize() 方法的定義: Public void Serialize(Stream,object);此方法接受stream類型以及object類型對象參數,將其中object類型對象序列化之後,傳遞到第1個參數所指定的數據流對象。下面是還原序列化的方法Deserialize() 的定義,這個方法將上面序列化分解的對象重新組合還原,以取得原始對象: Public object Deserialize(Stream serializationStream);其中Stream類型參數便是所要還原的數據流,Deserialize方法從這個數據流對象中重新組合還原被序列化分解的對象。

2.SoapFormatter類用來將對象序列化分解爲SOAP格式以及還原序列化對象,這個類同樣提供序列化對象以及還原序列化操作的方法,名稱同樣爲Serialize() 以及Deserializ() ,甚至所接受的參數值與上面的BinaryFormatter類也相同。

當要將一個類的實例對象進行序列化分解之前,首先必須要確認這個類是否可以進行序列化分解,一個類通常通過屬性 [Serializable] 將其標註爲可序列化(XmlSerializer方法可以不標註而直接序列化),換句話說,可以依據所要序列化分解的格式,使用上面任一個類對標註爲 [Serializable] 的類實例對象進行序列化分解。當然,在序列化對象時,也可以使用屬性 [NonSerialized] ,將對象中的某些成員標註爲不可序列化,有選擇性的只分解對象中的某些數據成員,那些標註爲 [NonSerialized] 的數據成員,在對象的序列化過程中將不會被分解。

3. XmlSerializer類用來將對象序列化分解爲XML格式以及還原序列化對象,實現方法同上。

XmlSerializer支持很多特性(ATTRIBUTES),它們可以用於爲特定的類設定序列化方式。例如,可將某個字段或屬性標以 [XmlIgnore] 特性,從而將其排除在序列化機制之外。另外 [XmlElement] 可用於指定XML元素名稱(這個元素名稱被用於特定的屬性或字段)。

對於經由SoapFormatter,BinaryFormatter的序列化,也可以使用特性進行某種程度的控制。例如,[NonSerialized] 特性等價於XmlSerializer的 [XmlIgnore] 特性。對序列化過程的終極控制,可以通過在其實休打算被序列化的類上,實現ISerializable接口。XmlSerializer不能用於序列化任何實現了IDictionary接口的類的實體,比方說Hashtable(散列表),而SoapFormatter和BinaryFormatter卻沒有這個限制。

微軟將XmlSerializer用於Web Services,而將BinaryFormatter和SoapFormatter用於遠程化。

序列化類使用選擇         可以依據具體情況來進行有效。XmlSerializer的限制比較嚴格,比如,要求目標類有一個無參數的構造器,並且只有公開的讀,寫屬性和字段時纔可被序列化。不過,從好的方面說,XmlSerializer對定製XML文檔提供了良好的支持。XmlSerializer的特性意味着,它最適合用在跨平臺的情況下,或者用於從現有的XML文檔構建對象。SoapFormatter和BinaryFormatter比XmlSerializer的限制要少,它們可以序列化私有字段。然而,它們都需要目標類標以 [Serializable] 特性,因此,像XmlSerializer一樣,需要小心以序列化方式來編寫類。有些詭異的東西也是要小心提防的。在反序列化時,新對象的構造器並不被調用。對SoapFormatter和BinaryFormatter的選擇,取決於應用。BinaryFormatter對於序列化和反序列化都是發生在.NET平臺上並且執行性能很重要的情況大有用處。通常來說,在所有其它情況下,SoapFormatter更有意義,也更易於調試。

自定義序列化自定義序列化行爲          序列化與還原序列化均由指定的Formetter自動完成。也可以選擇實現ISerializable接口,自行創建專屬的Formetter,以達到自行控制對象序列化及還原序列化的目的。在某些情形下,我們特別需要序列化的過程,例如若變量值的內容在序列化之後會失去原先存儲的值,此時你便可以自定義序列化的行爲,強迫恢復原先的內容。你可以在對象序列化的過程中,進一步使用加密技術,改變數據的格式,在反序列化操作的時候,以相對的解密技術將數據還原。自定義序列化的行爲允許你設計序列化對象的過程,而不是直接將指定的對象分解然後放入網絡或是存儲到數據流,避免數據的遺失,甚至在序列化進程發生不當擷取的情形。 自定義序列化方法實現ISerializable接口可以幫助你自定義對象的序列化行爲,繼承這個接口的類,必須同時實現方法GetObjectData() 和Constructor構造函數。

GetObjectData() 用來將對象相關信息填入SerializationInfo對象,其定義如下: Void GetObjectData(SerializationInfo info,StreamingContext context);其中的info是SerializationInfo類型的對象參數。SerializationInfo類被設計用來存儲對象自定義序列化操作時所需的信息,context是一種StreamingContext結構類型參數,表示序列化操作的來源數據流。

除了實現GetObjectData() 方法外,自定義序列化的過程,還必須完成特定構造函數的實現,構造函數在對象還原序列化時,將上面的info 參數內容傳遞到類。實現構造函數,必須引用SerializationInfo所定義的實例方法GetValue() ,提供指定的成員變量值。其特定構造函數形式如下: Public 類名(SerializationInfo info,StreamingContext context);

ISerializable接口位於System.Runtime.Serialization命名空間,在實現接口之前,必須引用這個命名空間。

序列化屬性的繼承          對象的序列化屬性沒有辦法被繼承,換句話說,繼承標註爲 [Serialized] 類的子類,由其創建的實例對象,並沒有辦法被序列化,除非你明確地將其標註爲[Serialized],如果基類同時亦實現接口ISerializable,派生的子類同樣也必須實現方法GetObjectData() 以及構造函數,才能夠自定義序列化成員變量的相關程序。另外在還原序列化重組對象時所調用的構造函數,同樣必須繼承基類。

序列化數據的修正          當一個成員變量被標註爲 [NonSerialized] 時,由於在對象序列化的過程中,成員變量並沒有被分解,當進行還原序列化操作,即重組對象的時候,所得到的將不是原先的成員變量的值。如果想要改變這種預設行爲,可以在設計類的時候,繼承IDeserializationCallback接口,實現方法OnDeserialization() ,還原正確的數據值。

方法OnDeserialization() 的定義如下: Void OnDeserialization(object sender);

繼承IDeserializationCallback接口的類對象,在還原序列化的時候,會先調用OnDeserialization() 方法,執行其中的方法。

當我們操作一個包含大量的暫時性數據對象,而此時序列化對象的時候,會同時分解這些暫時性數據,連同分解後的對象一併寫入文件,可以想象,這種作法非但沒有意義,而且浪費資源。這時可以選擇先把這些數據成員標註爲 [NonSerialized] ,然後在還原序列化,重組對象的同時,調用方法OnDeserialization() ,還原先前未被序列化分解的成員變量,這樣一來,除了序列化的過程比較有效率外,還能夠兼顧數據的正確性。

 序列化示例  序列化操作時需要引用的命名空間  

using System.Runtime.Serialization; //包含可用於序列化和反序列化對象的類
using System.Runtime.Serialization.Formatters.Binary; //二進制序列化需要的命名空間
using System.Runtime.Serialization.Formatters.Soap; //Soap序列化需要的命名空間
using System.Xml.Serialization; //XML序列化需要的命名空間

BinaryFormatter方法

/* 定義一個簡單的測試類,用來測試Binary和Soap方式序列化和反序列化 */
[Serializable] /* 定義類MText屬性爲可序列化類 */
public class MText
 {
     public string str;
 }
 

 

 

 

 

/* BinarySerialize方法將執行二進制序列化操作 */
public void BinarySerialize(MText mtx)
{
     /* 創建文件流fs,並定義保存文件BinarySeri.txt */
FileStream fs = new FileStream("BinarySeri.txt", FileMode.Create);
/* 實例化二進制序列化操作類BinaryFormatter */
BinaryFormatter bf = new BinaryFormatter();
/* 調用Serialize方法進行二進制序列化操作,並將mtx的可序列化成員內容寫進文件流fs      中進行存放 */
bf.Serialize(fs, mtx);
/* 關閉文件流 */
fs.Close();
}
/* BinaryDeSerialize方法將執行二進制反序列化操作,並返回序列化類MText */
public MText BinaryDeSerialize()
{
     /* 定義並實例化MText類,用來保存反序列化得到的結果 */
     MText mtx = new MText();
/* 創建文件流fs,並定義讀取文件BinarySeri.txt */
     FileStream fs = new FileStream("BinarySeri.txt", FileMode.Open);
     /* 實例化二進制序列化操作類BinaryFormatter */
     BinaryFormatter bf = new BinaryFormatter();
     /* 調用Deserialize方法進行二進制反序列化操作,並將轉換後的結果賦給MText
        變量mtx */
     mtx = (MText)bf.Deserialize(fs);
     /* 關閉文件流並反回結果 */
     fs.Close();
     return mtx;
}
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SoapFormatter方法

     /* SoapFormatter方法與二進制序列化和反序列化大體相同,只是序列化時
        所用的操作類由BinaryFormatter變爲SoapFormatter,其它一致 */
     Text mtx = new MText();
     SoapFormatter sf = new SoapFormatter();
     sf.Serialize(fs, mtx); //序列化操作
     mtx = (MText)sf.Deserialize(fs); //反序列化操作
  

 

 

 

 

 

XmlSerialize方法

 

 

 

         /* XmlSerialize方法與二進制序列化和反序列化大體相同,只是序列化時
          所用的操作類由BinaryFormatter變爲XmlSerialize,其它一致 */
         MText mtx = new MText();
     /* 在實例化XmlSerializer時,需要給出由typeof函數返回的可序列化對象的類型 */
XmlSerializer xs = new XmlSerializer(typeof(MText));
     xs.Serialize(fs, mtx); //序列化操作
mtx = (MText)xs.Deserialize(fs); //反序列化操作

方法詳見二進制序列化。

 ISerializable方法

/* 定義一個簡單的測試類,用來測試ISerializable方式序列化和反序列化 */
[Serializable/* 定義類RMText爲可序列化類 */
public class RMText : ISerializable /* 繼承ISerializable接口創建專屬Formatter */
{
   public string str;
  public RMText() { }
   /* 特定構造函數,用來在對象反序列化時,將info參數內容傳送到類 */
   public RMText(SerializationInfo info, StreamingContext context)
   {
       str = (string)info.GetString("str");
   }
   /* 繼承ISerializable接口所必須實現的方法,GetObjectData用來將對象相關信息填入
      SerializationInfo對象。在這個方法中,將指定的自定義序列化成員變量加入info,
      強制Formatter序列化這些成員變量爲當前的內容,成員變量以key/value的形式加入
      Info,也就是其中的每一個值均需有其對應的名稱 */
   public virtual void GetObjectData(SerializationInfo info,
StreamingContext context)
   {
        info.AddValue("str", str);
   }
}

 

 

 

 

 

 

 

 

 

 

 

 自定義序列化類由任何可進行序列化操作的方法進行直接使用。

Inherit(繼承)方法

[Serializable//定義類IMText爲可序列化類
public class IMText : RMText //繼承RMText類
{
     public string istr;
     public IMText() { }
     /*特定構造函數,用來在對象反序列化時,將info參數內容傳送到類,並繼承基類*/
     public IMText(SerializationInfo info, StreamingContext context)
           : base(info, context)
     {
          istr = (string)info.GetString("istr");
     }
     /* 繼承ISerializable接口所必須實現的方法,GetObjectData用來將對象相關信息填        入SerializationInfo對象。在這個方法中,將指定的自定義序列化成員變量加入 
Info,強制Formatter序列化這些成員變量爲當前的內容,成員變量以key/value的形式加入Info,也就是其中的每一個值均需有其對應的名稱,並覆寫基類的方法*/
      public override void GetObjectData(SerializationInfo info,
 StreamingContext context)
      {
            base.GetObjectData(info, context);
            info.AddValue("istr", istr);
       }
}

 

 

 

 

 

 

 

 

 

 

 

 繼承序列化類由任何可進行序列化操作的方法進行直接使用。

IDeserializationCallback方法

[Serializable] //定義類NonMText爲可序列化類
/* 繼承接口IDeserializationCallback,在其中實現OnDeserialization方法還原正確的
   數據 */
public class NonMText : IDeserializationCallback
{
     /* 標註成員變量爲不可序列化 */
    [NonSerialized]
     public int num;
    [NonSerialized]
     public int tmp;
     public NonMText()
     {
         num = 1000;
         tmp = 2000;
      }
      /* 當Formatter還原序列化,重組對象時,會先調用OnDeserialization方法,重新
         指定num變量的值,而tmp由於沒有做修正,得到的結果將不同於原值2000 */
      public void OnDeserialization(object sender)
      {
          num = 1000;
       }
}
發佈了47 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章