天天看點

C# XML序列化/反序列化參考

C#的XML序列化/反序列化的一些常用操作。

.NET提供了很不錯的XML序列化/反序列化器,(它們所在的命名空間為System.Xml.Serialization)這是很友善的,下面對它的使用做一些總結,以供參考。

以上代碼是序列化為字元串,如果需要以流的形式傳回序列化結果給用戶端,或寫入檔案,那麼通常需要選擇一種編碼,常見的編碼格式是UTF-8,但某些特殊場合也許你會被要求使用GB2312編碼,下面例子是使用GB2312編碼的情況:

這樣就直接把對象以特定編碼格式序列化到MemoryStream裡去了,當然也許你想到了,先使用前面的SerializeXml生成字元串,再把字元串以特定編碼格式寫到流或者位元組數組中去不行嗎?當然行,不過這樣會多出一步,不夠直接。

這裡還有個要注意的地方,序列化到流的時候,不要對Stream及TextWriter對象包在using裡,因為這樣會導緻流傳回的時候已經被關閉。

其中Department是你要反序列化出來的類,同樣需要注意編碼,這裡指定的是UTF-8,但不排除有别的可能。

其實序列化和反序列化時可逆的,你通過怎樣的類和編碼把對象序列化成xml,就能通過怎樣的類和編碼将xml反序列化成對象。

通過XmlRoot注解和XmlElement注解即可實作,其中XmlRoot用于指定“根”,也就是XML的最上一層的Tag。

利用XmlAttribute注解,這麼一來,Timestamp就成為了department這個根節點的timestamp屬性。

預設情況下,xml的頭會帶上類似這樣的一個namespace:

你不需要的話可以修改一下序列化方法:

上面提到的Utf8Writer是指定要用UTF-8的編碼輸出,包括xml頭部的encoding屬性,的代碼如下:

順便提一下,一般情況下,我們都會使用UTF-8編碼,但也有特殊情況(在對接一些曆史遺留系統時)下是需要用GBK (現在和GB2312算是同義) 的,這種情況下,Encoding改為:

這個怎麼說呢?先看這麼一個類:

序列化出來的結果是:

注意Employee這個标簽外面包了一層Employees,這個也許不是你想要的結果,這才是你想要的結果:

這個怎麼做呢?很簡單,在Employees前面加個XmlElement注解即可:

另外,如果是隻是想改一下之前的Employees标簽的名字的話,用這樣一個注解:[XmlArray("NewName")]。

預設情況下,null值的屬性是不會被序列化的,想想看為什麼?

因為生成<DeptName />這樣的序列化結果的話,沒辦法知道DeptName到底是null還是空字元串,是以比較好的解決方法是在序列化之前,把null字元串填充為空字元串。可以考慮寫一個幫助方法,利用反射周遊一個對象裡的所有字元串屬性,将null設定為空字元串,當然了,實際的情況要考慮得更全面點,比如對象裡還有對象,而且還包含可枚舉對象的情況,估計得使用遞歸。篇幅問題,代碼我就不貼了。

另外還有一種比較道地的做法,不需要改變對象的值,那就是在對象上加上[XmlElement(IsNullable = true)]注解,但這樣帶來的問題就是會在序列化生成的tag中多出一個xsi:nil="true"這樣的屬性來。

有些情況實在太特殊,沒辦法直接用簡單的Deserialize方法來反序列化,例如這個XML:

首先根節點很奇葩,預設反序列化器不認,另外就是IGAAUC,重複多次,它的意圖是說重複的這幾個IGAAUC拼接在一起,生成一個位址,這個預設的反序列化顯然做不到,手工讀吧,參考代碼如下:

預設情況下,如果XML的标簽中的内容為空的話,标簽就“自閉合”(self-closing),如:

而我們想要這樣的效果:

那就需要自己對XmlWriter進行一些修改,用一個新的序列化的方法:

上面的代碼中還包括了XmlWriterSettings,它可以指定縮進,換行等格式。對于XmlWriter,用XmlWriterForceFullEnd進行一下包裝,XmlWriterForceFullEnd的代碼如下:

其實關鍵的就是WriteEndElement這個方法,一行代碼而已,但由于XmlWriter 是抽象類,很多虛方法得這麼轉一下。