天天看點

CSDN 的序列化

序列化

序列化是将對象狀态轉換為可保持或傳輸的形式的過程。序列化的補集是反序列化,後者将流轉換為對象。這兩個過程一起保證資料易于存儲和傳輸。

NET Framework 提供了兩個序列化技術:

  • 二進制序列化保持類型保真,這對于多次調用應用程式時保持對象狀态非常有用。例如,通過将對象序列化到剪貼闆,可在不同的應用程式之間共享對象。您可以将對象序列化到流、磁盤、記憶體和網絡等。遠端處理使用序列化,“按值”在計算機或應用程式域之間傳遞對象。
  • XML 序列化隻序列化公共屬性和字段,并且不保持類型保真。當您希望提供或使用資料而不限制使用該資料的應用程式時,這一點非常有用。由于 XML 是開放式的标準,是以它對于通過 Web 共享資料來說是一個理想選擇。SOAP 同樣是開放式的标準,這使它也成為一個理想選擇。

二進制序列化

可以将序列化定義為一個将對象狀态存儲到存儲媒體的過程。在這個過程中,對象的公共字段和私有字段以及類(包括含有該類的程式集)的名稱,将轉換成位元組流,而位元組流接着将寫入資料流。随後對該對象進行反序列化時,将建立原始對象的準确克隆。

在面向對象的環境中實作序列化機制時,必須多在易用性與靈活性之間做出權衡。很大程度上,這個過程可以自動完成,但前提是您對該過程擁有足夠的控制權。例如,如果簡單的二進制序列化不足,或者可能有特定原因決定需要對類中的哪些字段進行序列化,可能就會出現這種情況。以下章節說明了随 .NET Framework 一起提供的可靠序列化機制,并強調了根據需要自定義該過程所能使用的一些重要功能。

序列化概念

為什麼要使用序列化?兩個最重要的原因是将對象狀态儲存到存儲媒體,以便可以在以後階段重新建立精确副本;以及将對象按值從一個應用程式域發送至另一個應用程式域。例如,序列化用于在 ASP.NET 中儲存會話狀态,并将對象複制到 Windows 窗體的剪貼闆中。它還可用于在遠端進行中将對象按值從一個應用程式域傳遞至另一個應用程式域。

經常有必要将對象的字段值存儲至磁盤,以後再檢索此資料。盡管不依賴序列化也能很容易地實作這一點,但方法通常麻煩而容易出錯,并且需要跟蹤對象的層次結構時會逐漸變得更加複雜。假設要編寫一個包含數千個對象的大型商務應用程式,并且必須為每個對象編寫代碼,以便将字段和屬性儲存至磁盤以及從磁盤進行還原。序列化為實作這一目标提供了友善的機制。

公共語言運作庫可管理對象在記憶體中存儲的方式,并通過使用反射提供一種自動序列化機制。當序列化對象時,類的名稱、程式集和類執行個體的所有資料成員被寫入存儲區。對象經常以成員變量方式将引用存儲至其他執行個體。當序列化類時,序列化引擎跟蹤被引用的對象(已序列化),以確定同一對象不會被多次序列化。随 .NET Framework 一起提供的序列化體系結構可自動正确地處理對象圖和循環引用。對于對象圖的唯一要求是,也必須将已序列化的對象引用的所有對象标記為 Serializable(有關更多資訊,請參見基本序列化)。如果未進行标記,序列化程式嘗試序列化未标記的對象時将引發異常。

當反序列化已序列化的類時,将重新建立該類,并且将自動還原所有資料成員的值。

[Serializable]

public class MyObject {

  public int n1 = 0;

  public int n2 = 0;

  public String str = null;

}

MyObject obj = new MyObject();

obj.n1 = 1;

obj.n2 = 24;

obj.str = "Some String";

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);

formatter.Serialize(stream, obj);

stream.Close();

此示例使用二進制格式化程式執行序列化。您隻需要建立流的執行個體以及要使用的格式化程式,然後在該格式化程式上調用 Serialize 方法。要序列化的流和對象将作為參數提供給此調用。盡管未在此示例中明确示範,但類的所有成員變量都将被序列化,即使将變量标記為私有也是如此。在這一方面,二進制序列化與 XMLSerializer 類不同,後者隻序列化公共字段。有關從二進制序列化排除成員變量的資訊,請參見有選擇的序列化。

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin", FileMode.Open, FileAccess.Read, FileShare.Read);

MyObject obj = (MyObject) formatter.Deserialize(stream);

stream.Close();

// Here's the proof.

Console.WriteLine("n1: {0}", obj.n1);

Console.WriteLine("n2: {0}", obj.n2);

Console.WriteLine("str: {0}", obj.str);

上面使用的 BinaryFormatter 非常有效,并可産生壓縮位元組流。使用此格式化程式序列化的所有對象也可使用它進行反序列化,這使得它成為對将在 .NET Framework 上反序列化的對象進行序列化的理想工具。需要特别注意的是,反序列化對象時不調用構造函數。這是出于性能原因而對反序列化進行的限制。然而,這違反了運作庫對對象編寫器制定的某些常用協定,并且将對象标記為可序列化時開發人員應確定了解其後果。

如果要求可移植,請改用 SoapFormatter。隻需要将以上代碼中的 BinaryFormatter 替換為 SoapFormatter,然後與前面一樣調用 Serialize 和 Deserialize。此格式化程式針對以上使用的示例産生下面的輸出:

<SOAP-ENV:Envelope

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:xsd="http://www.w3.org/2001/XMLSchema"

  xmlns:SOAP- ENC="http://schemas.xmlsoap.org/soap/encoding/"

  xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle=

  "http://schemas.microsoft.com/soap/encoding/clr/1.0"

  "http://schemas.xmlsoap.org/soap/encoding/"

  xmlns:a1="http://schemas.microsoft.com/clr/assem/ToFile">

  <SOAP-ENV:Body>

    <a1:MyObject id="ref-1">

      <n1>1</n1>

      <n2>24</n2>

      <str id="ref-3">Some String</str>

    </a1:MyObject>

  </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

需要特别注意的是,無法繼承 Serializable 屬性。如果從 MyObject 派生新類,新類也必須标記為以上屬性,否則将無法序列化。例如,嘗試序列化以下類的執行個體時,将收到 SerializationException,通知您 MyStuff 類型未标記為可序列化。

public class MyStuff : MyObject

{

  public int n3;

}

使用 Serializable 屬性非常友善,但有以上所述的限制。有關何時應标記類以進行序列化的資訊,請參見序列化準則;在編譯類後不能對類進行序列化。

有選擇的序列化

類通常包含不應進行序列化的字段。例如,假定類将線程 ID 存儲在成員變量中。如果反序列化該類,而在對該類進行序列化時,存儲了該 ID 的線程可能不再運作。是以,序列化該值毫無意義。您可以防止對成員變量進行序列化,方法是使用 NonSerialized 屬性标記它們,如下所述。

 [Serializable]

public class MyObject

{

  public int n1;

  [NonSerialized] public int n2;

  public String str;

}

如有可能,應使可能包含安全敏感資料的對象不可序列化。如果必須對該對象進行序列化,可将 NonSerialized 屬性應用于存儲敏感資料的特定字段。如果沒有将這些字段排除在序列化之外,應該注意它們存儲的資料将向有權序列化的所有代碼公開。

序列化準則

由于編譯類之後不能使類可序列化,是以設計新的類時應考慮序列化。有些要問的問題是:需要跨應用程式域發送此類嗎?此類可用于遠端處理嗎?使用者将對此類進行哪些操作?他們會從需要序列化的我的類派生新類嗎?當無法确定時,請将此類标記為可序列化。除非可确定以下任一情況,否則可能最好将所有類标記為可序列化。

  • 類永遠不會跨應用程式域。如果不需要序列化并且類需要跨應用程式域,則請從 MarshalByRefObject 派生類。
  • 類存儲僅适用于它的目前執行個體的特殊指針。例如,如果某個類包含非托管記憶體或檔案句柄,則請確定用 NonSerializedAttribute 屬性标記這些檔案,或者根本不序列化該類。
  • 類資料成員包含敏感資訊。在此情況下,建議将類标記為可序列化,但建議用 NonSerializedAttribute 屬性标記包含敏感資訊的各資料成員。另一個方法是實作 ISerializable 接口并隻序列化所需的字段。

請注意将類标記為可序列化的安全含義。類或類構造函數上 CodeAccessPermission 的 Link Demand 或 Inheritance Demand 可以在預設情況下或者由自定義序列化跳過,該序列化可實作對同一 CodeAccessPermission 的相應需求。(有關更多資訊,請參見 SecurityAction 枚舉)。如果類具有權限的 Link Demand,則運作庫隻檢查直接調用方來驗證該權限是否已授予調用方。使用 Microsoft 強名稱對 .NET Framework 類庫代碼進行簽名,并始終對它授予完全信任。任何代碼都可使用被授予完全信任的代碼來跳過連結時的安全檢查。例如,在序列化情況下,沒有所需序列化權限的惡意代碼可調用其中一個完全信任的 .NET Framework 格式化程式(如 BinaryFormatter),并跳過連結時對權限的檢查。

XML 和 SOAP 序列化

XML 序列化将對象的公共字段和屬性或者方法的參數及傳回值轉換(序列化)為符合特定 XML 架構定義語言 (XSD) 文檔的 XML 流。XML 序列化會生成強類型類,同時将公共屬性和字段轉換為序列格式(在此情況下為 XML),以便存儲或傳輸。

由于 XML 是開放式的标準,是以可以根據需要由任何應用程式處理 XML 流,而與平台無關。例如,用 ASP.NET 建立的 XML Web services 使用 XmlSerializer 類來建立 XML 流,這些流在整個 Internet 中或在 Intranet 上的 XML Web services 應用程式之間傳遞資料。相反,反序列化采用這樣一個 XML 流并重新構造對象。

XML 序列化還可用于将對象序列化為符合 SOAP 規範的 XML 流。SOAP 是一種基于 XML 的協定,它是專門為使用 XML 來傳輸過程調用而設計的。

要序列化或反序列化對象,請使用 XmlSerializer 類。要建立待序列化的類,請使用 XML 架構定義工具。

XML 序列化簡介

序列化是将對象轉換成易于傳輸的形式的過程。例如,可以序列化對象,并使用 HTTP 通過 Internet 在用戶端和伺服器之間進行傳輸。另一方面,反序列化在流中重新建構對象。

XML 序列化隻将對象的公共字段和屬性值序列化為 XML 流。XML 序列化不包括類型資訊。例如,如果 Library 命名空間中存在 Book 對象,則不能保證将它反序列化為同一類型的對象。

注意:
XML 序列化不能轉換方法、索引器、私有字段或隻讀屬性(隻讀集合除外)。要序列化對象的所有公共和私有字段和屬性,請使用 BinaryFormatter 而不要使用 XML 序列化。

XML 序列化中的中心類是 XmlSerializer 類,該類中最重要的方法是 Serialize 和 Deserialize 方法。XmlSerializer 建立 C# 檔案并将其編譯為 .dll 檔案,以執行此序列化。在 .NET Framework 2.0 中,XML 序列化程式生成器工具 (Sgen.exe) 旨在預先生成這些序列化程式集,以便與應用程式一起部署并改善啟動性能。XmlSerializer 生成的 XML 流符合網際網路聯合會 (www.w3.org) XML 架構定義語言 (XSD) 1.0 建議書。而且,生成的資料類型符合文檔“XML 架構第 2 部分:資料類型”(XML Schema Part 2: Datatypes)。

對象中的資料是用程式設計語言構造來描述的,如類、字段、屬性 (Property)、基元類型、數組,甚至 XmlElement 或 XmlAttribute 對象形式的嵌入 XML。您可以建立自己的用屬性 (Attribute) 批注的類,也可以使用 XML 架構定義工具生成基于現有 XML 架構的類。

如果有 XML 架構,則可以運作 XML 架構定義工具生成一組類,将這組類的類型強制為此架構,并用屬性 (Attribute) 進行批注。當序列化這種類的執行個體時,生成的 XML 符合 XML 架構。對于這種類,可以采用易于操作的對象模型進行程式設計,同時確定生成的 XML 符合 XML 架構。這是使用 .NET Framework 中的其他類(如 XmlReader 和 XmlWriter 類)分析和編寫 XML 流的另一種方法。有關更多資訊,請參見 XML Documents and Data。這些類可讓您分析任何 XML 流。相反,如果預期 XML 流符合已知的 XML 架構,則使用 XmlSerializer。

屬性可控制 XmlSerializer 類生成的 XML 流,進而允許您設定 XML 流的 XML 命名空間、元素名稱、屬性名稱等。有關這些屬性及其如何控制 XML 序列化的更多資訊,請參見使用屬性控制 XML 序列化。有關用于控制生成的 XML 的那些屬性所組成的表,請參見用來控制 XML 序列化的屬性。

XmlSerializer 類可以進一步序列化對象并生成編碼的 SOAP XML 流。生成的 XML 符合網際網路聯合會文檔“簡單對象通路協定 (SOAP) 1.1”(Simple Object Access Protocol (SOAP) 1.1) 的第 5 節。有關此過程的更多資訊,請參見如何:将對象序列化為 SOAP 編碼的 XML 流。有關控制生成的 XML 的屬性所組成的表,請參見用來控制編碼的 SOAP 序列化的屬性。

XmlSerializer 類生成由 XML Web services 建立并傳遞給 XML Web services 的 SOAP 消息。要控制 SOAP 消息,可以将屬性應用于在 XML Web services 檔案 (.asmx) 中找到的類、傳回值、參數和字段。由于 XML Web services 可以使用文本或編碼的 SOAP 樣式,是以既可以使用“用來控制 XML 序列化的屬性”中列出的屬性,也可以使用“用來控制編碼的 SOAP 序列化的屬性”中列出的屬性。有關使用屬性來控制由 XML Web services 生成的 XML 的更多資訊,請參見使用 XML Web services 進行 XML 序列化。有關 SOAP 和 XML Web services 的更多資訊,請參見Customizing SOAP Messages。

XmlSerializer 應用程式的安全注意事項

建立應用程式來使用 XmlSerializer 時,應注意以下幾項及其含義:

  • XmlSerializer 在 TEMP 環境變量命名的目錄中建立 C# (.cs) 檔案并将其編譯為 .dll 檔案;這些 DLL 檔案将進行序列化。
注意:
可預先生成這些序列化程式集,并使用 SGen.exe 工具對它們進行簽名。這不适用于 Web 服務的伺服器。也就是說,這隻适用于用戶端用法和手動序列化。
  • 代碼和 DLL 在建立和編譯時易受惡意程序破壞。當使用的計算機運作 Microsoft Windows NT 4.0 或更高版本時,可能有兩個或多個使用者共享 TEMP 目錄。如果兩個帳戶的安全權限不同,且權限較高的帳戶使用 XmlSerializer 運作應用程式,則共享 TEMP 目錄是危險的。在這種情況下,一個使用者可能因替換已編譯的 .cs 或 .dll 檔案而違反計算機的安全性。要消除這一問題,應始終確定計算機上的每個帳戶具有各自的配置檔案。預設情況下,TEMP 環境變量為每個帳戶指向不同的目錄。
  • 如果惡意使用者将連續的 XML 資料流發送至 Web 伺服器(拒絕服務攻擊),則 XmlSerializer 會持續處理資料,直至計算機資源不足。

如果您使用的計算機運作了 Internet 資訊服務 (IIS),并且您的應用程式在 IIS 内運作,則可消除這種攻擊。IIS 提供了一種網關,這種網關不處理長度超過設定量(預設值為 4 KB)的流。如果建立的應用程式不使用 IIS 并且使用 XmlSerializer 進行反序列化,則您應當實作類似的網關,以防止拒絕服務攻擊。

  • XmlSerializer 使用提供給它的任何類型序列化資料并運作任何代碼。

惡意對象造成威脅的方式有兩種。它可以運作惡意代碼,也可以将惡意代碼插入 XmlSerializer 所建立的 C# 檔案。在第一種情況下,如果惡意對象試圖運作破壞性的過程,代碼通路安全有助于防止發生任何損壞。在第二種情況下,理論上講惡意對象有可能以某種方式将代碼插入 XmlSerializer 所建立的 C# 檔案。盡管這一問題已得到徹底檢查并且認為不可能發生這種攻擊,但還是應當采取防範措施,一定不要使用未知和不受信任的類型來序列化資料。

  • 已序列化的敏感資料可能容易受到攻擊。

XmlSerializer 序列化資料後,資料可以存儲在 XML 檔案或其他資料存儲區中。如果資料存儲區可供其他程序使用或者可在 Intranet 或 Internet 上看見,則資料可能被盜和惡意使用。例如,如果您建立一個應用程式來序列化包含信用卡号碼的訂單,則資料是高度敏感的。為防止被盜和惡意使用,應始終保護您的資料存儲區并采取步驟保持其私密性。

簡單類的序列化

下面的代碼示例顯示具有公共字段的基類。

public class OrderForm

{

    public DateTime OrderDate;

}

此類的執行個體序列化後可能如下所示。

<OrderForm>

    <OrderDate>12/12/01</OrderDate>

</OrderForm>

可序列化的項

可使用 XmLSerializer 類對以下項進行序列化:

  • 公共類的公共讀/寫屬性和字段。
  • 實作 ICollection 或 IEnumerable 的類。
注意:
僅序列化集合,不序列化公共屬性。
  • XmlElement 對象。
  • XmlNode 對象。
  • DataSet 對象。

有關序列化或反序列化對象的更多資訊,請參見如何:序列化對象和如何:反序列化對象。

使用 XML 序列化的優點

将對象序列化為 XML 時,XmlSerializer 類可為您提供完整而靈活的控制。如果要建立 XML Web services,可以将控制序列化的屬性應用于類和成員,以確定 XML 輸出符合特定架構。

例如,XmlSerializer 可讓您:

  • 指定字段或屬性 (Property) 是否應編碼為屬性 (Attribute) 或元素。
  • 指定要使用的 XML 命名空間。
  • 指定字段或屬性 (Property) 名稱不恰當時的元素或屬性 (Attribute) 名稱。

XML 序列化的另一個優點是,隻要生成的 XML 流符合給定的架構,就對開發的應用程式沒有任何限制。假設有一個架構,它用于描述書籍。它提供有書名、作者、出版商和 ISBN 号元素。您可以開發一個應用程式來以任何想要的方式(例如以書籍訂單或書籍庫存方式)處理 XML 資料。在任一種情況下,唯一的要求是 XML 流符合指定的 XML 架構定義語言 (XSD) 架構。

XML 序列化注意事項

使用 XmlSerializer 類時,應注意以下事項:

  • Sgen.exe 工具特别設計為生成序列化程式集,以獲得最佳性能。
  • 序列化資料隻包含資料本身和類的結構。類型辨別和程式集資訊不包括在内。
  • 隻能序列化公共屬性和字段。屬性必須具有公共通路器(get 和 set 方法)。如果必須序列化非公共資料,請使用 BinaryFormatter 類而不是 XML 序列化。
  • 類必須具有預設構造函數才能被 XmlSerializer 序列化。
  • 方法不能被序列化。
  • 如果可實作 IEnumerable 或 ICollection 的類滿足特定要求,則 XmlSerializer 可以以不同方式處理這些類,如下所述。

實作 IEnumerable 的類必須實作采用單個參數的公共 Add 方法。Add 方法的參數必須與從 IEnumerator.Current 屬性傳回的類型一緻(多态),該屬性是從 GetEnumerator 方法傳回的。

除了 IEnumerable 之外,實作 ICollection(如 CollectionBase)的類還必須有一個采用整數的公共 Item 索引屬性(在 C# 中為索引器),而且必須有一個 integer 類型的公共 Count 屬性。傳遞給 Add 方法的參數的類型必須與從 Item 屬性傳回的類型相同,或者為此類型的基之一。

對于實作 ICollection 的類,可從已編制索引的 Item 屬性檢索要序列化的值,而不是通過調用 GetEnumerator。此外,除了傳回另一個集合類(實作 ICollection 的類)的公共字段外,公共字段和屬性不會被序列化。有關示例,請參見 XML 序列化示例。

XSD 資料類型映射

網際網路聯合會 (www.w3.org) 文檔“XML 架構第 2 部分:資料類型”(XML Schema Part 2: Datatypes) 指定 XML 架構定義語言 (XSD) 架構所允許的簡單資料類型。對于其中的許多資料類型(如 int 和 decimal),.NET Framework 中有相應的資料類型。然而,某些 XML 資料類型在 .NET Framework 中沒有相應的資料類型(如 NMTOKEN 資料類型)。在這種情況下,如果使用 XML 架構定義工具 (XML 架構定義工具 (Xsd.exe)) 從架構中生成類,相應的屬性 (Attribute) 會應用于字元串類型的成員,其 DataType 屬性 (Property) 會設定為 XML 資料類型名稱。例如,如果架構包含 XML 資料類型為 NMTOKEN 且名為“MyToken”的元素,則生成的類可能包含下例所示的成員。

Visual Basic

<XmlElement(DataType:="NMTOKEN")> _

Public MyToken As String

[XmlElement(DataType = "NMTOKEN")]

public string MyToken;

同樣,如果要建立的類必須符合特定的 XML 架構 (XSD),則應當應用相應的屬性 (Attribute) 并将其 DataType 屬性 (Property) 設定為所需的 XML 資料類型名稱。

有關類型映射的完整清單,請參見以下任一屬性 (Attribute) 類的 DataType 屬性 (Property)。

  • SoapAttributeAttribute
  • SoapElementAttribute
  • XmlArrayItemAttribute
  • XmlAttributeAttribute
  • XmlElementAttribute
  • XmlRootAttribute

如何:序列化對象

·         要序列化對象,首先應建立要序列化的對象,然後設定其公共屬性和字段。為此,必須确定 XML 流的傳輸格式,即它是作為流還是作為檔案進行存儲。例如,如果 XML 流必須以永久形式儲存,則應建立 FileStream 對象。

MySerializableClass myObject = new MySerializableClass();

// Insert code to set properties and fields of the object.

XmlSerializer mySerializer = new

XmlSerializer(typeof(MySerializableClass));

// To write to a file, create a StreamWriter object.

StreamWriter myWriter = new StreamWriter("myFileName.xml");

mySerializer.Serialize(myWriter, myObject);

myWriter.Close();

如何:反序列化對象

當您反序列化對象時,傳輸格式确定您将建立流還是檔案對象。确定了傳輸格式之後,就可以根據需要調用 Serialize 或 Deserialize 方法。

反序列化對象

1.        使用要反序列化的對象的類型構造 XmlSerializer。

2.        調用 Deserialize 方法以生成該對象的副本。在反序列化時,必須将傳回的對象強制轉換為原始對象的類型,如下面的示例中所示,該示例将該對象反序列化為檔案(盡管也可以将該對象反序列化為流)。

MySerializableClass myObject;

// Construct an instance of the XmlSerializer with the type

// of object that is being deserialized.

XmlSerializer mySerializer =

new XmlSerializer(typeof(MySerializableClass));

// To read the file, create a FileStream.

FileStream myFileStream =

new FileStream("myFileName.xml", FileMode.Open);

// Call the Deserialize method and cast to the object type.

myObject = (MySerializableClass)

mySerializer.Deserialize(myFileStream)

如何:重寫編碼的 SOAP XML 序列化

将對象的 XML 序列化重寫為 SOAP 消息的過程類似于重寫标準 XML 序列化的過程。有關重寫标準 XML 序列化的資訊,請參見如何:指定 XML 流的替代元素名稱。

将對象的序列化重寫為 SOAP 消息

1.        建立 SoapAttributeOverrides 類的執行個體。

2.        為正在序列化的每個類成員建立 SoapAttributes。

3.        對正在序列化的成員适當建立影響 XML 序列化的一個或多個屬性的執行個體。有關更多資訊,請參見“用來控制編碼的 SOAP 序列化的屬性”。

4.        将 SoapAttributes 的相應屬性 (Property) 設定為在步驟 3 中建立的屬性 (Attribute)。

5.        将 SoapAttributes 添加至 SoapAttributeOverrides。

6.        使用 SoapAttributeOverrides 建立 XmlTypeMapping。使用 SoapReflectionImporterImportTypeMapping 方法。

7.        使用 XmlTypeMapping 建立 XmlSerializer。

8.        序列化或反序列化對象。

示例

下面的代碼示例以兩種方式序列化檔案:第一種方式,不重寫 XmlSerializer 類的行為;第二種方式,重寫該行為。示例包含帶有幾個成員的名為 Group 的類。已将各個屬性(如 SoapElementAttribute)應用于類成員。當已使用 SerializeOriginal 方法序列化該類時,屬性會控制 SOAP 消息的内容。調用 SerializeOverride 方法後,XmlSerializer 的行為會被重寫,方法是建立各個屬性 (Attribute) 并根據需要将 SoapAttributes 的屬性 (Property) 設定為這些屬性 (Attribute)。

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.Xml.Schema;

public class Group

{

    [SoapAttribute (Namespace = "http://www.cpandl.com")]

    public string GroupName;

    [SoapAttribute(DataType = "base64Binary")]

    public Byte [] GroupNumber;

    [SoapAttribute(DataType = "date", AttributeName = "CreationDate")]

    public DateTime Today;

    [SoapElement(DataType = "nonNegativeInteger", ElementName = "PosInt")]

    public string PostitiveInt;

    // This is ignored when serialized unless it is overridden.

    [SoapIgnore]

    public bool IgnoreThis;

    public GroupType Grouptype;

    [SoapInclude(typeof(Car))]

    public Vehicle myCar(string licNumber)

    {

        Vehicle v;

        if(licNumber == "")

            {

                v = new Car();

            v.licenseNumber = "!!!!!!";

        }

        else

        {

            v = new Car();

            v.licenseNumber = licNumber;

        }

        return v;

    }

}

public abstract class Vehicle

{

    public string licenseNumber;

    public DateTime makeDate;

}

public class Car: Vehicle

{

}

public enum GroupType

{

    // These enums can be overridden.

    small,

    large

}

public class Run

{

    public static void Main()

    {

        Run test = new Run();

        test.SerializeOriginal("SoapOriginal.xml");

        test.SerializeOverride("SoapOverrides.xml");

        test.DeserializeOriginal("SoapOriginal.xml");

        test.DeserializeOverride("SoapOverrides.xml");

    }

    public void SerializeOriginal(string filename)

    {

        // Creates an instance of the XmlSerializer class.

        XmlTypeMapping myMapping =

        (new SoapReflectionImporter().ImportTypeMapping(

        typeof(Group)));

        XmlSerializer mySerializer = 

        new XmlSerializer(myMapping);

        // Writing the file requires a TextWriter.

        TextWriter writer = new StreamWriter(filename);

        // Creates an instance of the class that will be serialized.

        Group myGroup = new Group();

        // Sets the object properties.

        myGroup.GroupName = ".NET";

        Byte [] hexByte = new Byte[2]{Convert.ToByte(100),

        Convert.ToByte(50)};

        myGroup.GroupNumber = hexByte;

        DateTime myDate = new DateTime(2002,5,2);

        myGroup.Today = myDate;

        myGroup.PostitiveInt= "10000";

        myGroup.IgnoreThis=true;

        myGroup.Grouptype= GroupType.small;

        Car thisCar =(Car)  myGroup.myCar("1234566");

        // Prints the license number just to prove the car was created.

        Console.WriteLine("License#: " + thisCar.licenseNumber + "/n");

        // Serializes the class and closes the TextWriter.

        mySerializer.Serialize(writer, myGroup);

        writer.Close();

    }

    public void SerializeOverride(string filename)

    {

        // Creates an instance of the XmlSerializer class

        // that overrides the serialization.

        XmlSerializer overRideSerializer = CreateOverrideSerializer();

        // Writing the file requires a TextWriter.

        TextWriter writer = new StreamWriter(filename);

        // Creates an instance of the class that will be serialized.

        Group myGroup = new Group();

        // Sets the object properties.

        myGroup.GroupName = ".NET";

        Byte [] hexByte = new Byte[2]{Convert.ToByte(100),

        Convert.ToByte(50)};

        myGroup.GroupNumber = hexByte;

        DateTime myDate = new DateTime(2002,5,2);

        myGroup.Today = myDate;

        myGroup.PostitiveInt= "10000";

        myGroup.IgnoreThis=true;

        myGroup.Grouptype= GroupType.small;

        Car thisCar =(Car)  myGroup.myCar("1234566");

        // Serializes the class and closes the TextWriter.

        overRideSerializer.Serialize(writer, myGroup);

         writer.Close();

    }

    public void DeserializeOriginal(string filename)

    {

        // Creates an instance of the XmlSerializer class.

        XmlTypeMapping myMapping =

        (new SoapReflectionImporter().ImportTypeMapping(

        typeof(Group)));

        XmlSerializer mySerializer = 

        new XmlSerializer(myMapping);

        TextReader reader = new StreamReader(filename);

        // Deserializes and casts the object.

        Group myGroup;

        myGroup = (Group) mySerializer.Deserialize(reader);

        Console.WriteLine(myGroup.GroupName);

        Console.WriteLine(myGroup.GroupNumber[0]);

        Console.WriteLine(myGroup.GroupNumber[1]);

        Console.WriteLine(myGroup.Today);

        Console.WriteLine(myGroup.PostitiveInt);

        Console.WriteLine(myGroup.IgnoreThis);

        Console.WriteLine();

    }

    public void DeserializeOverride(string filename)

    {

        // Creates an instance of the XmlSerializer class.

        XmlSerializer overRideSerializer = CreateOverrideSerializer();

        // Reading the file requires a TextReader.

        TextReader reader = new StreamReader(filename);

        // Deserializes and casts the object.

        Group myGroup;

        myGroup = (Group) overRideSerializer.Deserialize(reader);

        Console.WriteLine(myGroup.GroupName);

        Console.WriteLine(myGroup.GroupNumber[0]);

        Console.WriteLine(myGroup.GroupNumber[1]);

        Console.WriteLine(myGroup.Today);

        Console.WriteLine(myGroup.PostitiveInt);

        Console.WriteLine(myGroup.IgnoreThis);

    }

    private XmlSerializer CreateOverrideSerializer()

    {

        SoapAttributeOverrides mySoapAttributeOverrides =

        new SoapAttributeOverrides();

        SoapAttributes soapAtts = new SoapAttributes();

        SoapElementAttribute mySoapElement = new SoapElementAttribute();

        mySoapElement.ElementName = "xxxx";

        soapAtts.SoapElement = mySoapElement;

        mySoapAttributeOverrides.Add(typeof(Group), "PostitiveInt",

        soapAtts);

        // Overrides the IgnoreThis property.

        SoapIgnoreAttribute myIgnore = new SoapIgnoreAttribute();

        soapAtts = new SoapAttributes();

        soapAtts.SoapIgnore = false;     

        mySoapAttributeOverrides.Add(typeof(Group), "IgnoreThis",

        soapAtts);

        // Overrides the GroupType enumeration.

        soapAtts = new SoapAttributes();

        SoapEnumAttribute xSoapEnum = new SoapEnumAttribute();

        xSoapEnum.Name = "Over1000";

        soapAtts.SoapEnum = xSoapEnum;

        // Adds the SoapAttributes to the

        // mySoapAttributeOverridesrides.

        mySoapAttributeOverrides.Add(typeof(GroupType), "large",

        soapAtts);

        // Creates a second enumeration and adds it.

        soapAtts = new SoapAttributes();

        xSoapEnum = new SoapEnumAttribute();

        xSoapEnum.Name = "ZeroTo1000";

        soapAtts.SoapEnum = xSoapEnum;

        mySoapAttributeOverrides.Add(typeof(GroupType), "small",

        soapAtts);

        // Overrides the Group type.

        soapAtts = new SoapAttributes();

        SoapTypeAttribute soapType = new SoapTypeAttribute();

        soapType.TypeName = "Team";

        soapAtts.SoapType = soapType;

        mySoapAttributeOverrides.Add(typeof(Group),soapAtts);

        // Creates an XmlTypeMapping that is used to create an instance

        // of the XmlSerializer class. Then returns the XmlSerializer.

        XmlTypeMapping myMapping = (new SoapReflectionImporter(

        mySoapAttributeOverrides)).ImportTypeMapping(typeof(Group));

        XmlSerializer ser = new XmlSerializer(myMapping);

        return ser;

    }

}