.NET物件的XML序列化和反序列化

yzf01發表於2021-09-09

序列化的概念
序列化是指一個物件的例項可以被儲存,儲存成一個二進位制串,當然,一旦被儲存成二進位制串,那麼也可以儲存成文字串了。
比如,一個計數器,數值為2,我們可以用字串“2”表示。
如果有個物件,叫做connter,當前值為2,那麼可以序列化成“2”,反向的,也可以從“2”得到值為2的計數器例項。
這樣,關機時序列化它,開機時反序列化它,每次開機都是延續的。不會都是從頭開始。

序列化概念的提出和實現,可以使我們的應用程式的設定資訊儲存和讀取更加方便。

序列化有很多好處,比如,在一臺機器上產生一個例項,初始化完畢,然後可以序列化,透過網路傳送到另一臺機器,然後反序列化,得到物件例項,之後再執行某些業務邏輯,得到結果,再序列化,返回第一臺機器,第一臺機器得到物件例項,得到結果。
這個例子是目前比較先進的“智慧代理”的原理。

當前比較熱火的web services使用soap協議,soap協議也是以物件的可序列化為基礎的。

一 概述
.NET Framework為處理XML資料提供了許多不同的類庫。XmlDocument類能讓你像處理檔案一樣處理xml資料,而XmlReader、XmlWriter和它們的派生類使你能夠將xml資料作為資料流處理。
XmlSerializer則提供了另外的方法,它使你能夠將自己的物件序列化和反序列化為xml。序列化資料既能夠讓你像處理檔案一樣對資料進行隨機處理,同時又能跳過你不感興趣的資料。
二 主要類庫介紹
   .NET 支援物件xml序列化和反序列化的類庫主要位於名稱空間System.Xml.Serialization中。
   1.  XmlSerializer 類
   該類用一種高度鬆散耦合的方式提供序列化服務。你的類不需要繼承特別的基類,而且它們也不需要實現特別的介面。相反,你只需在你的類或者這些類的公共域以及讀/寫屬性里加上自定義的特性。XmlSerializer透過反射機制讀取這些特性並用它們將你的類和類成員對映到xml元素和屬性。
   2. XmlAttributeAttribute 類
   指定類的公共域或讀/寫屬性對應xml檔案的Attribute。
   例:[XmlAttribute(“type”)] or [XmlAttribute(AttributeName=”type”)]
   3. XmlElementAttribute類
   指定類的公共域或讀/寫屬性對應xml檔案的Element。
   例:[XmlElement(“Maufacturer”)] or [XmlElement(ElementName=”Manufacturer”)]
   4. XmlRootAttribute類
   Xml序列化時,由該特性指定的元素將被序列化成xml的根元素。
   例:[XmlRoot(“RootElement”)] or [XmlRoot(ElementName = “RootElements”)]
   5. XmlTextAttribute 類
   Xml序列化時,由該特性指定的元素值將被序列化成xml元素的值。一個類只允許擁有一個該特性類的例項,因為xml元素只能有一個值。
   6. XmlIgnoreAttribute類
   Xml序列化時不會序列化該特性指定的元素。
三 例項
   下面例子中的xml schema 描述了一個簡單的人力資源資訊,其中包含了xml的大部分格式,如xml 元素相互巢狀, xml元素既有元素值,又有屬性值。
   1. 待序列化的類層次結構
   

 [XmlRoot("humanResource")]
    public class HumanResource
    {
        #region private data.
        private int m_record = 0;
        private Worker[] m_workers = null;
        #endregion
 
        [XmlAttribute(AttributeName="record")]
        public int Record
        {
            get { return m_record; }
            set { m_record = value; }
        }
 
        [XmlElement(ElementName="worker")]
        public Worker[] Workers
        {
            get { return m_workers; }
            set { m_workers = value; }
        }
}
 
    public class Worker
    {
        #region private data.
        private string m_number = null;
        private InformationItem[] m_infoItems = null;
        #endregion
 
        [XmlAttribute("number")]
        public string Number
        {
            get { return m_number; }
            set { m_number = value; }
        }
 
        [XmlElement("infoItem")]
        public InformationItem[] InfoItems
        {
            get { return m_infoItems; }
            set { m_infoItems = value; }
        }
}
 
    public class InformationItem
    {
        #region private data.
        private string m_name = null;
        private string m_value = null;
        #endregion
 
        [XmlAttribute(AttributeName = "name")]
        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }
 
        [XmlText]
        public string Value
        {
            get { return m_value; }
            set { m_value = value; }
        }
}


   2. 序列化生成的xml結構
      


                 Michale
                 male
                 25
            
                 Surce
                 male
                 28
           


   3. 利用XmlSerializer類進行序列化和反序列化的實現(一般利用快取機制實現xml檔案只解析一次。)
   

public sealed class ConfigurationManager
    {
        private static HumanResource m_humanResource = null;
        private ConfigurationManager(){}
 
        public static HumanResource Get(string path)
        {
            if (m_humanResource == null)
            {
                FileStream fs = null;
                try
                {
                    XmlSerializer xs = new XmlSerializer(typeof(HumanResource));
                    fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                    m_humanResource = (HumanResource)xs.Deserialize(fs);
                    fs.Close();
                    return m_humanResource;
                }
                catch
                {
                    if (fs != null)
                        fs.Close();
                    throw new Exception("Xml deserialization failed!");
                }
 
            }
            else
            {
                return m_humanResource;
            }
        }
 
        public static void Set(string path, HumanResource humanResource)
        {
            if (humanResource == null)
                throw new Exception("Parameter humanResource is null!");
            
            FileStream fs = null;
            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(HumanResource));
                fs = new FileStream(path, FileMode.Create, FileAccess.Write);
                xs.Serialize(fs, humanResource);
                m_humanResource = null;
                fs.Close();
            }
            catch
            {
                if (fs != null)
                    fs.Close();
                throw new Exception("Xml serialization failed!");
            }
        }
    }


四 說明
   1. 需要序列化為xml元素的屬性必須為讀/寫屬性;
   2. 注意為類成員設定預設值,儘管這並不是必須的。
 有關.NET中序列化的一些知識
    “序列化”可被定義為將物件的狀態儲存到儲存媒介中的過程。在此過程中,物件的公共欄位和私有欄位以及類的名稱(包括包含該類的程式集)都被轉換為位元組流,然後寫入資料流。在以後“反序列化”該物件時,建立原始物件的精確複本。
一、為什麼要選擇序列化
    一個原因是將物件的狀態保持在儲存媒體中,以便可以在以後重新建立精確的副本;
    另一個原因是透過值將物件從一個應用程式域傳送到另一個應用程式域中。
    例如,序列化可用於在 ASP.NET 中儲存會話狀態並將物件複製到 Windows 窗體的剪貼簿中。遠端處理還可以使用序列化透過值將物件從一個應用程式域傳遞到另一個應用程式域中。
二、如何實現物件的序列化及反序列化
    要實現物件的序列化,首先要保證該物件可以序列化。而且,序列化只是將物件的屬性進行有效的儲存,對於物件的一些方法則無法實現序列化的。
    實現一個類可序列化的最簡便的方法就是增加Serializable屬性標記類。如:
    [Serializable()]
    public class MEABlock
    {
        private int m_ID;
        public string Caption;

        public MEABlock()
        {
            ///建構函式
        }
    }
    即可實現該類的可序列化。
    要將該類的例項序列化為到檔案中?.NET FrameWork提供了兩種方法:
    1、XML序列化
        使用 XmLSerializer 類,可將下列項序列化。

公共類的公共讀/寫屬性和欄位
實現 ICollection 或 IEnumerable 的類。(注意只有集合會被序列化,而公共屬性卻不會。)
XmlElement 物件。
XmlNode 物件。
DataSet 物件。
         要實現上述類的例項的序列化,可參照如下例子:
        MEABlock myBlock = new MEABlock();
        // Insert code to set properties and fields of the object.
        XmlSerializer mySerializer = new XmlSerializer(typeof(MEABlock));
        // To write to a file, create a StreamWriter object.
        StreamWriter myWriter = new StreamWriter("myFileName.xml");
        mySerializer.Serialize(myWriter, MEABlock);
    需要注意的是XML序列化只會將public的欄位儲存,對於私有欄位不予於儲存。
    生成的XML檔案格式如下:
       
            Test
       

    對於物件的反序列化,則如下:
        MEABlock myBlock;
        // Constructs an instance of the XmlSerializer with the type
        // of object that is being deserialized.
        XmlSerializer mySerializer = new XmlSerializer(typeof(MEABlock));
        // To read the file, creates a FileStream.
        FileStream myFileStream = new FileStream("myFileName.xml", FileMode.Open);
        // Calls the Deserialize method and casts to the object type.
        myBlock = (MEABlock)mySerializer.Deserialize(myFileStream)
    2、二進位制序列化
        與XML序列化不同的是,二進位制序列化可以將類的例項中所有欄位(包括私有和公有)都進行序列化操作。這就更方便、更準確的還原了物件的副本。
        要實現上述類的例項的序列化,可參照如下例子:
        MEABlock myBlock = new MEABlock();
        // Insert code to set properties and fields of the object.
        IFormatter formatter = new BinaryFormatter();
        Stream stream = new FileStream("MyFile.bin",FileMode.Create,FileAccess.Write, FileShare.None);
        formatter.Serialize(stream, myBlock);
        stream.Close();
    對於物件的反序列化,則如下:
        IFormatter formatter = new BinaryFormatter();
        Stream stream = new FileStream("MyFile.bin", FileMode.Open,FileAccess.Read, FileShare.Read);
        MEABlock myBlock = (MEABlock) formatter.Deserialize(stream);
        stream.Close();

三、如何變相實現自定義視覺化控制元件的序列化、反序列化
        對於WinForm中自定義控制元件,由於繼承於System.Windows.Form類,而Form類又是從MarshalByRefObject繼承的,窗體本身無法做到序列化,窗體的實現基於Win32下GUI資源,不能脫離當前上下文存在。
        當然可以採用變通的方法實現控制元件的序列化。這裡採用的是記憶類模型。
        定義記憶類(其實就是一個可序列化的實體類)用於記錄控制元件的有效屬性,需要序列化控制元件的時候,只需要將該控制元件的例項Copy到記憶類,演變成序列化儲存該記憶類的操作。
        反序列化是一個逆過程。將資料流反序列化成為該記憶類,再根據該記憶類的屬性生成控制元件例項。而對於控制元件的一些事件、方法則可以繼續使用。

 介紹 XML 序列化
 
.NET Framework 開發員指南  

序列化是將物件轉換為容易傳輸的格式的過程。例如,可以序列化一個物件,然後使用 HTTP 透過 Internet 在客戶端和伺服器之間傳輸該物件。在另一端,反序列化將從該流重新構造物件。
XML 序列化僅將物件的公共欄位和屬性值序列化為 XML 流。XML 序列化不包括型別資訊。例如,如果您有一個存在於 Library 名稱空間中的 Book 物件,將不能保證它將會被反序列化為同一型別的物件。

注意   XML 序列化不轉換方法、索引器、私有欄位或只讀屬性(只讀集合除外)。若要序列化物件的所有欄位和屬性(公共的和私有的),請使用 BinaryFormatter,而不要使用 XML 序列化。
XML 序列化中最主要的類是 XmlSerializer 類,它的最重要的方法是 Serialize 和 Deserialize 方法。XmlSerializer 生成的 XML 流符合全球資訊網聯合會 () XML 架構定義語言 (XSD) 1.0 的建議。另外,生成的資料型別符合標題為“XML Schema Part 2: Datatypes”(XML 架構第二部分:資料型別)的文件。

物件中的資料是用程式語言構造(如類、欄位、屬性、基元型別、陣列,甚至 XmlElement 或 XmlAttribute 物件形式的嵌入 XML)來描述的。您可以建立自己的用屬性批註的類,或者使用 XML 架構定義工具生成基於現有 XML 架構的類。

如果您有 XML 架構,就可執行 XML 架構定義工具生成一組強型別化為架構並用屬性批註的類。當序列化這樣的類的例項時,生成的 XML 符合 XML 架構。使用這樣的類,就可針對容易操作的物件模型進行程式設計,同時確保生成的 XML 符合 XML 架構。這是使用 .NET Framework 中的其他類(如 XmlReader 和 XmlWriter 類)分析和寫 XML 流的一種替換方法。(有關使用這些類的更多資訊,請參見使用 .NET Framework 中的 XML。)這些類使您可以分析任何 XML 流。與此相反,當需要 XML 流符合已知的 XML 架構時,請使用 XmlSerializer。

屬性控制由 XmlSerializer 類生成的 XML 流,使您可以設定 XML 流的 XML 名稱空間、元素名、屬性名等。有關這些屬性和它們如何控制 XML 序列化的更多資訊,請參見使用屬性控制 XML 序列化。若想獲得一個列示對所生成的 XML 起控制作用的那些屬性的表,請參見控制 XML 序列化的屬性。

XmlSerializer 類可進一步序列化物件並生成編碼的 SOAP XML 流。生成的 XML 符合標題為“Simple Object Access Protocol (SOAP) 1.1”的全球資訊網聯合會文件的第 5 節。有關此過程的更多資訊,請參見用 XML 序列化生成 SOAP 訊息。有關控制生成的 XML 的屬性表,請參見控制編碼的 SOAP 序列化的屬性。

XmlSerializer 類生成由 XML Web services 建立和傳遞給 XML Web services 的 SOAP 訊息。若要控制 SOAP 訊息,可將屬性應用於 XML Web services 檔案 (.asmx) 中的類、返回值、引數和欄位。您可以同時使用在“控制 XML 序列化的屬性”中列出的屬性和在“控制編碼的 SOAP 序列化的屬性”中列出的屬性,因為 XML Web services 可以使用文字樣式,也可以使用編碼的 SOAP 樣式。有關使用屬性控制 XML Web services 所生成的 XML 的更多資訊,請參見 XML Web services 的 XML 序列化。有關 SOAP 和 XML Web services 的更多資訊,請參見自定義 SOAP 訊息。

保護 XmlSerializer 應用程式
在建立使用 XmlSerializer 的應用程式時,您應當瞭解以下幾點以及它們的影響:

XmlSerializer 建立 C# 檔案 (.cs 檔案),並將其編譯成 .dll 檔案,這些 .dll 檔案位於由 TEMP 環境變數指定的目錄中;這些 DLL 檔案將發生序列化。
程式碼和 DLL 在建立和進行編譯時,易於遭受惡意程式的攻擊。如果所使用的計算機執行的是 Microsoft Windows NT 4.0 或更高版本,則有可能會有兩個或更多使用者共享臨時目錄。如果同時存在以下兩種情況,則共享臨時目錄是有危險性的:(1) 兩個帳戶有不同的安全特權;(2) 具有較高特權的帳戶執行一個使用 XmlSerializer 的應用程式。在這種情況下,某一使用者可以替換所編譯的 .cs 或 .dll 檔案,由此破壞計算機的安全性。為了避免發生這一問題,請始終確保計算機上的每一帳戶都有自己的配置檔案。如果能夠保證這一點的話,預設情況下,TEMP 環境變數就會為不同的帳戶指定不同的目錄。

如果一名惡意使用者向 Web 伺服器傳送持續的 XML 資料流(拒絕服務攻擊),XmlSerializer 會一直處理這一資料,直到計算機資源不夠用才停止。
如果您所使用的計算機執行 Internet 資訊服務 (IIS),並且您的應用程式是在 IIS 下執行,就可以避免這類攻擊。IIS 帶有一個控制門,用於禁止處理大於設定數量(預設值是 4 KB)的資料流。如果您所建立的應用程式不使用 IIS,同時該應用程式使用 XmlSerializer 進行反序列化,則應該實現一個類似的控制門,以阻止拒絕服務攻擊。

XmlSerializer 將使用給予它的任何型別,對資料進行序列化,並執行任何程式碼。
惡意物件施加威脅的方式有兩種。一種是執行惡意程式碼,另一種是將惡意程式碼插入到由 XmlSerializer 建立的 C# 檔案中。在第一種情況下,如果惡意物件試圖執行破壞性過程,程式碼訪問安全性將幫助防止發生任何破壞。在第二種情況下,在理論上,惡意物件有可能會以某種方式將程式碼插入到由 XmlSerializer 建立的 C# 檔案中。儘管對這一問題已進行了徹底的檢驗,而且這類攻擊被認為是不可能的,但您還是應該小心一些,一定不要序列化那些不可信的未知型別的資料。

已序列化的重要資料可能易於遭到攻擊。
XmlSerializer 對資料進行了序列化之後,資料可以被儲存為 XML 檔案,或儲存在其他資料儲存區。如果其他程式可以訪問到您的資料儲存區,或是可以在 Intranet 或 Internet 上看到該資料儲存區,資料就可能被竊取,並被惡意使用。例如,如果您建立了一個應用程式,對包含信用卡號碼的訂單進行序列化,這一資料就非常重要。為了防止發生這一問題,請始終保護您的資料儲存區,並對資料採取保密措施。

簡單類的序列化
下面的示例顯示一個具有公共欄位的簡單類:

[Visual Basic]Public Class OrderForm    Public OrderDate As DateTimeEnd Class[C#]public class OrderForm{    public DateTime OrderDate;}當將此類的例項序列化時,該例項可能類似於下面的程式碼:

    12/12/01有關序列化的更多示例,請參見 XML 序列化的示例。

可以序列化的項
使用 XmLSerializer 類,可將下列項序列化。

公共類的公共讀/寫屬性和欄位
實現 ICollection 或 IEnumerable 的類。(注意只有集合會被序列化,而公共屬性卻不會。)
XmlElement 物件。
XmlNode 物件。
DataSet 物件。
序列化和反序列化物件
若要序列化物件,首先建立要序列化的物件並設定它的公共屬性和欄位。為此,必須確定要用以儲存 XML 流的傳輸格式(或者作為流,或者作為檔案)。例如,如果 XML 流必須以永久形式儲存,則建立 FileStream 物件。當您反序列化物件時,傳輸格式確定您將建立流還是檔案物件。確定了傳輸格式之後,就可以根據需要呼叫 Serialize 或 Deserialize 方法。

序列化物件

建立物件並設定它的公共欄位和屬性。
使用該物件的型別構造 XmlSerializer。有關更多資訊,請參見 XmlSerializer 類建構函式。
呼叫 Serialize 方法以生成物件的公共屬性和欄位的 XML 流表示形式或檔案表示形式。下面的示例建立一個檔案。
[Visual Basic]Dim myObject As MySerializableClass = New MySerializableClass()' Insert code to set properties and fields of the object.Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(MySerializableClass))' To write to a file, create a StreamWriter object.Dim myWriter As StreamWriter = New StreamWriter("myFileName.xml")mySerializer.Serialize(myWriter, myObject)[C#]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);反序列化物件

使用要反序列化的物件的型別構造 XmlSerializer。
呼叫 Deserialize 方法以產生該物件的副本。在反序列化時,必須將返回的物件強制轉換為原始物件的型別,如下面的示例中所示。下面的示例將該物件反序列化為檔案,儘管它也可以被反序列化為流。
[Visual Basic]Dim myObject As MySerializableClass' Constructs an instance of the XmlSerializer with the type' of object that is being deserialized.Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(MySerializableClass))' To read the file, creates a FileStream.Dim myFileStream As FileStream = _New FileStream("myFileName.xml", FileMode.Open)' Calls the Deserialize method and casts to the object type.myObject = CType( _mySerializer.Deserialize(myFileStream), MySerializableClass)[C#]MySerializableClass myObject;// Constructs an instance of the XmlSerializer with the type// of object that is being deserialized.XmlSerializer mySerializer = new XmlSerializer(typeof(MySerializableClass));// To read the file, creates a FileStream.FileStream myFileStream = new FileStream("myFileName.xml", FileMode.Open);// Calls the Deserialize method and casts to the object type.myObject = (MySerializableClass) mySerializer.Deserialize(myFileStream)有關 XML 序列化的更多示例,請參見 XML 序列化的示例。

使用 XML 序列化的好處
XmlSerializer 類在您將物件序列化為 XML 時為您提供完整而靈活的控制。如果您正在建立 XML Web services,則可以將控制序列化的屬性應用於類和成員以確保 XML 輸出符合特定的架構。

例如,XmlSerializer 使您能夠:

指定應將欄位或屬性編碼為特性還是元素。
指定要使用的 XML 名稱空間。
如果欄位或屬性名不合適,則指定元素或特性的名稱。
XML 序列化的另一個好處是:只要生成的 XML 流符合給定的架構,則對於所開發的應用程式就沒有約束。假定有這樣一個用於描述圖書的架構,它具有標題、作者、出版商和 ISBN 編號元素。您可以開發一個以您希望的任何方式(例如,作為圖書訂單,或作為圖書清單)處理 XML 資料的應用程式。在任一種情況下,唯一的要求是 XML 流應當符合指定的 XML 架構定義語言 (XSD) 架構。

XML 序列化注意事項
使用 XmlSerializer 類時,應考慮下列情況:

序列化資料只包含資料本身以及類的結構。不包括型別標識和程式集資訊。
只能序列化公共屬性和欄位。如果需要序列化非公共資料,請使用 BinaryFormatter 類而不是 XML 序列化。
類必須有一個將由 XmlSerializer 序列化的預設建構函式。
不能序列化方法。
XmlSerializer 可以以不同方式處理實現 IEnumerable 或 ICollection 的類(條件是這些類滿足某些要求)。實現 IEnumerable 的類必須實現帶單個引數的公共 Add 方法。Add 方法的引數必須與從 GetEnumerator 方法返回的 IEnumerator.Current 屬性所返回的型別一致(多型)。除實現 IEnumerable 外還實現 ICollection 的類(如 CollectionBase)必須有一個取整數的公共 Item 索引屬性(在 C# 中為索引器),並且它必須有一個整數型別的公共 Count 屬性。傳遞給 Add 方法的引數必須與從 Item 屬性返回的型別相同或與該型別的某個基的型別相同。對於實現 ICollection 的類,要序列化的值將從索引 Item 屬性檢索,而不是透過呼叫 GetEnumerator 來檢索。另外請注意,除返回另一個集合類(實現 ICollection 的集合類)的公共欄位之外,將不序列化公共欄位和屬性。有關示例,請參見 XML 序列化的示例。
XSD 資料型別對映
標題為“XML Schema Part 2: Datatypes”的全球資訊網聯合會 () 文件指定在 XML 架構定義語言 (XSD) 架構中允許使用的簡單資料型別。對於這些資料型別中的許多型別(例如,int 和 decimal),在 .NET Framework 中都有對應的資料型別。但是,某些 XML 資料型別在 .NET Framework 中沒有對應的資料型別(例如,NMTOKEN 資料型別)。在這樣的情況下,如果使用 XML 架構定義工具 (Xsd.exe) 從架構生成類,就會將適當的特性應用於字串型別的成員,並會將其 DataType 屬性設定為 XML 資料型別名稱。例如,如果架構包含一個資料型別為 XML 資料型別 NMTOKEN 的、名為“MyToken”的元素,則生成的類可能包含以下示例中的成員。

[Visual Basic]Public MyToken As String[C#][XmlElement(DataType = "NMTOKEN")]public string MyToken;與此類似,如果建立一個必須符合特定 XML 架構 (XSD) 的類,應當應用適當的特性並將它的 DataType 屬性設定為所需的 XML 資料型別名稱。

有關型別對映的完整列表,請參見下列任意一個特性類的 DataType 屬性:SoapAttributeAttribute、SoapElementAttribute、XmlArrayItemAttribute、XmlAttributeAttribute、XmlElementAttribute 或 XmlRootAttribute。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2334/viewspace-2799961/,如需轉載,請註明出處,否則將追究法律責任。

相關文章