在.net中序列化讀寫xml方法的總結
在.net中序列化讀寫xml方法的總結
閱讀目錄
- 開始
- 最簡單的使用XML的方法
- 類型定義與XML結構的映射
- 使用 XmlElement
- 使用 XmlAttribute
- 使用 InnerText
- 重命名節點名稱
- 列表和數組的序列化
- 列表和數組的做為數據成員的序列化
- 類型繼承與反序列化
- 反序列化的實戰演練
- 反序列化的使用總結
- 排除不需要序列化的成員
- 強制指定成員的序列化順序
- 自定義序列化行為
- 序列化去掉XML命名空間及聲明頭
- XML的使用建議
XML是一種很常見的數據保存方式,我經常用它來保存一些數據,或者是一些配置參數。 使用C#,我們可以借助.net framework提供的很多API來讀取或者創建修改這些XML, 然而,不同人使用XML的方法很有可能並不相同。 今天我打算談談我使用XML的一些方法,供大家參考。
最簡單的使用XML的方法
由於.net framework針對XML提供了很多API,這些API根據不同的使用場景實現了不同層次的封裝, 比如,我們可以直接使用XmlTextReader、XmlDocument、XPath來取數XML中的數據, 也可以使用LINQ TO XML或者反序列化的方法從XML中讀取數據。 那麽,使用哪種方法最簡單呢?
我個人傾向於使用序列化,反序列化的方法來使用XML。 采用這種方法,我只要考慮如何定義數據類型就可以了,讀寫XML各只需要一行調用即可完成。 例如:
// 1. 首先要創建或者得到一個數據對象 Order order = GetOrderById(123); // 2. 用序列化的方法生成XML string xml = XmlHelper.XmlSerialize(order, Encoding.UTF8); // 3. 從XML讀取數據並生成對象 Order order2 = XmlHelper.XmlDeserialize<Order>(xml, Encoding.UTF8);
就是這麽簡單的事情,XML結構是什麽樣的,我根本不用關心, 我只關心數據是否能保存以及下次是否能將它們讀取出來。
說明:XmlHelper是一個工具類,全部源代碼如下:
或許有人會說:我使用XPath從XML讀取數據也很簡單啊。
我認為這種說法有一個限制條件:只需要從XML中讀取少量的數據。
如果要全部讀取,用這種方法會寫出一大堆的機械代碼出來! 所以,我非常反感用這種方法從XML中讀取全部數據。
類型定義與XML結構的映射
如果是一個新項目,我肯定會毫不猶豫的使用序列化和反序列化的方法來使用XML, 然而,有時在維護一個老項目時,面對一堆只有XML卻沒有與之對應的C#類型時, 我們就需要根據XML結構來逆向推導C#類型,然後才能使用序列化和反序列化的方法。 逆向推導的過程是麻煩的,不過,類型推導出來之後,後面的事情就簡單多了。
為了學會根據XML結構逆向推導類型,我們需要關註一下類型定義與XML結構的映射關系。
註意:有時候我們也會考慮XML結構對於傳輸量及可閱讀性的影響,所以關註一下XML也是有必要的。
這裏有一個XML文件,是我從Visual Sutdio的安裝目錄中找到的:
怎樣用反序列化的方式來讀取它的數據呢,我在博客的最後將給出完整的實現代碼。
現在,我們還是看一下這個XML有哪些特點吧。
<LinkGroup ID="sites" Title="Venus Sites" Priority="1500">
對於這個節點來說,它包含了三個數據項(屬性):ID,Title,Priority。 這樣的LinkGroup節點有三個。
類似的還有Glyph節點。
<LItem URL="http://www.asp.net" LinkGroup="sites">ASP.NET Home Page</LItem>
LItem節點除了與LinkGroup有著類似的數據(屬性)之外,還包含著一個字符串:ASP.NET Home Page , 這是另外一種數據的存放方式。
另外,LinkGroup和LItem都允許重復出現,我們可以用數組或者列表(Array,List)來理解它們。
我還發現一些嵌套關系:LinkGroup可以包含Glyph,Context包含著Links,Links又包含了多個LItem。
不管如何嵌套,我發現數據都是包含在一個一個的XML節點中。
如果用專業的單詞來描述它們,我們可以將ID,Title,Priority這三個數據項稱為 XmlAttribute, LItem,LinkGroup節點稱為 XmlElement,”ASP.NET Home Page“出現的位置可以稱為 InnerText。 基本上,XML就是由這三類數據組成。
下面我來演示如何使用這三種數據項。
回到頂部使用 XmlElement
首先,我來定義一個類型:
public class Class1 { public int IntValue { get; set; } public string StrValue { get; set; } }
下面是序列化與反序列的調用代碼:
Class1 c1 = new Class1 { IntValue = 3, StrValue = "Fish Li" }; string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8); Console.WriteLine(xml); Console.WriteLine("---------------------------------------"); Class1 c2 = XmlHelper.XmlDeserialize<Class1>(xml, Encoding.UTF8); Console.WriteLine("IntValue: " + c2.IntValue.ToString()); Console.WriteLine("StrValue: " + c2.StrValue);
運行結果如下:
<?xml version="1.0" encoding="utf-8"?> <Class1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <IntValue>3</IntValue> <StrValue>Fish Li</StrValue> </Class1> --------------------------------------- IntValue: 3 StrValue: Fish Li
結果顯示,IntValue和StrValue這二個屬性生成了XmlElement。
小結:默認情況下(不加任何Attribute),類型中的屬性或者字段,都會生成XmlElement。
回到頂部使用 XmlAttribute
再來定義一個類型:
public class Class2 { [XmlAttribute] public int IntValue { get; set; } [XmlElement] public string StrValue { get; set; } }
註意,我在二個屬性上增加的不同的Attribute.
下面是序列化與反序列的調用代碼:
運行結果如下(我將結果做了換行處理):
<?xml version="1.0" encoding="utf-8"?> <Class2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IntValue="3"> <StrValue>Fish Li</StrValue> </Class2> --------------------------------------- IntValue: 3 StrValue: Fish Li
結果顯示:
1. IntValue 生成了XmlAttribute
2. StrValue 生成了XmlElement(和不加[XmlElement]的效果一樣,表示就是默認行為)。
小結:如果希望類型中的屬性或者字段生成XmlAttribute,需要在類型的成員上用[XmlAttribute]來指出。
回到頂部使用 InnerText
還是來定義一個類型:
public class Class3 { [XmlAttribute] public int IntValue { get; set; } [XmlText] public string StrValue { get; set; } }
註意,我在StrValue上增加的不同的Attribute.
下面是序列化與反序列的調用代碼:
運行結果如下(我將結果做了換行處理):
<?xml version="1.0" encoding="utf-8"?> <Class3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IntValue="3">Fish Li</Class3> --------------------------------------- IntValue: 3 StrValue: Fish Li
結果符合預期:StrValue屬性在增加了[XmlText]之後,生成了一個文本節點(InnerText)
小結:如果希望類型中的屬性或者字段生成InnerText,需要在類型的成員上用[XmlText]來指出。
回到頂部重命名節點名稱
看過前面幾個示例,大家應該能發現:通過序列化得到的XmlElement和XmlAttribute都與類型的數據成員或者類型同名。 然而有時候我們可以希望讓屬性名與XML的節點名稱不一樣,那麽就要使用【重命名】的功能了,請看以下示例:
[XmlType("c4")] public class Class4 { [XmlAttribute("id")] public int IntValue { get; set; } [XmlElement("name")] public string StrValue { get; set; } }
序列化與反序列的調用代碼前面已經多次看到,這裏就省略它們了。
運行結果如下(我將結果做了換行處理):
<?xml version="1.0" encoding="utf-8"?> <c4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="3"> <name>Fish Li</name> </c4> --------------------------------------- IntValue: 3 StrValue: Fish Li
看看輸出結果中的紅字粗體字,再看看類型定義中的三個Attribute的三個字符串參數,我想你能發現規律的。
小結:XmlAttribute,XmlElement允許接受一個別名用來控制生成節點的名稱,類型的重命名用XmlType來實現。
回到頂部列表和數組的序列化
繼續看示例代碼:
Class4 c1 = new Class4 { IntValue = 3, StrValue = "Fish Li" }; Class4 c2 = new Class4 { IntValue = 4, StrValue = "http://www.cnblogs.com/fish-li/" }; // 說明:下面二行代碼的輸出結果是一樣的。 List<Class4> list = new List<Class4> { c1, c2 }; //Class4[] list = new Class4[] { c1, c2 }; string xml = XmlHelper.XmlSerialize(list, Encoding.UTF8); Console.WriteLine(xml); // 序列化的結果,反序列化一定能讀取,所以就不再測試反序列化了。
運行結果如下:
<?xml version="1.0" encoding="utf-8"?> <ArrayOfC4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <c4 id="3"> <name>Fish Li</name> </c4> <c4 id="4"> <name>http://www.cnblogs.com/fish-li/</name> </c4> </ArrayOfC4>
現在c4節點已經重復出現了,顯然,是我們期待的結果。
不過,ArrayOfC4,這個節點名看起來太奇怪了,能不能給它也重命名呢?
繼續看代碼,我可以定義一個新的類型:
// 二種Attribute都可以完成同樣的功能。 //[XmlType("c4List")] [XmlRoot("c4List")] public class Class4List : List<Class4> { }
然後,改一下調用代碼:
Class4List list = new Class4List { c1, c2 };
運行結果如下:
<?xml version="1.0" encoding="utf-8"?> <c4List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <c4 id="3"> <name>Fish Li</name> </c4> <c4 id="4"> <name>http://www.cnblogs.com/fish-li/</name> </c4> </c4List>
小結:數組和列表都能直接序列化,如果要重命名根節點名稱,需要創建一個新類型來實現。
回到頂部列表和數組的做為數據成員的序列化
首先,還是定義一個類型:
public class Root { public Class3 Class3 { get; set; } public List<Class2> List { get; set; } }
序列化的調用代碼:
Class2 c1 = new Class2 { IntValue = 3, StrValue = "Fish Li" }; Class2 c2 = new Class2 { IntValue = 4, StrValue = "http://www.cnblogs.com/fish-li/" }; Class3 c3 = new Class3 { IntValue = 5, StrValue = "Test List" }; Root root = new Root { Class3 = c3, List = new List<Class2> { c1, c2 } }; string xml = XmlHelper.XmlSerialize(root, Encoding.UTF8); Console.WriteLine(xml);
運行結果如下:
<?xml version="1.0" encoding="utf-8"?> <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Class3 IntValue="5">Test List</Class3> <List> <Class2 IntValue="3"> <StrValue>Fish Li</StrValue> </Class2> <Class2 IntValue="4"> <StrValue>http://www.cnblogs.com/fish-li/</StrValue> </Class2> </List> </Root>
假設這裏需要為List和Class2的節點重命名,該怎麽辦呢?
如果繼續使用前面介紹的方法,是行不通的。
下面的代碼演示了如何重命名列表節點的名稱:
public class Root { public Class3 Class3 { get; set; } [XmlArrayItem("c2")] [XmlArray("cccccccccccc")] public List<Class2> List { get; set; } }
序列化的調用代碼與前面完全一樣,得到的輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Class3 IntValue="5">Test List</Class3> <cccccccccccc> <c2 IntValue="3"> <StrValue>Fish Li</StrValue> </c2> <c2 IntValue="4"> <StrValue>http://www.cnblogs.com/fish-li/</StrValue> </c2> </cccccccccccc> </Root>
想不想把cccccccccccc節點去掉呢(直接出現c2節點)?
下面的類型定義方式實現了這個想法:
public class Root { public Class3 Class3 { get; set; } [XmlElement("c2")] public List<Class2> List { get; set; } }
輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Class3 IntValue="5">Test List</Class3> <c2 IntValue="3"> <StrValue>Fish Li</StrValue> </c2> <c2 IntValue="4"> <StrValue>http://www.cnblogs.com/fish-li/</StrValue> </c2> </Root>
小結:數組和列表都在序列化時,默認情況下會根據類型中的數據成員名稱生成一個節點, 列表項會生成子節點,如果要重命名,可以使用[XmlArrayItem]和[XmlArray]來實現。 還可以直接用[XmlElement]控制不生成列表的父節點。
回到頂部類型繼承與反序列化
列表元素可以是同一種類型,也可以不是同一種類型(某個類型的派生類)。
例如下面的XML:
<?xml version="1.0" encoding="utf-8"?> <XRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <List> <x1 aa="1" bb="2" /> <x1 aa="3" bb="4" /> <x2> <cc>ccccccccccc</cc> <dd>dddddddddddd</dd> </x2> </List> </XRoot>
想像一下,上面這段XML是通過什麽類型得到的呢?
答案如下(註意紅色粗體部分):
public class XBase { } [XmlType("x1")] public class X1 : XBase { [XmlAttribute("aa")] public int AA { get; set; } [XmlAttribute("bb")] public int BB { get; set; } } [XmlType("x2")] public class X2 : XBase { [XmlElement("cc")] public string CC { get; set; } [XmlElement("dd")] public string DD { get; set; } } public class XRoot { [XmlArrayItem(typeof(X1)), XmlArrayItem(typeof(X2))] public List<XBase> List { get; set; } }
序列化代碼:
X1 x1a = new X1 { AA = 1, BB = 2 }; X1 x1b = new X1 { AA = 3, BB = 4 }; X2 x2 = new X2 { CC = "ccccccccccc", DD = "dddddddddddd" }; XRoot root = new XRoot { List = new List<XBase> { x1a, x1b, x2 } }; string xml = XmlHelper.XmlSerialize(root, Encoding.UTF8); Console.WriteLine(xml);
小結:同時為列表成員指定多個[XmlArrayItem(typeof(XXX))]可實現多種派生類型混在一起輸出。
回到頂部反序列化的實戰演練
接下來,我們將根據前面介紹的知識點,用反序列化的方法來解析本文開頭處貼出的那段XML:
那段XML的根元素是DynamicHelp,因此,我們需要定義一個類型,類名為DynamicHelp。
再觀察那段XML,它應該包含一個LinkGroup列表,和一個Context屬性,所以可以這樣定義這三個類型:
public class DynamicHelp { [XmlElement] public List<LinkGroup> Groups { get; set; } public Context Context { get; set; } } public class LinkGroup { } public class Context { }
再來看LinkGroup,它包含三個數據成員,以及一個子節點:Glyph,因此可以這樣定義它們:
public class LinkGroup { [XmlAttribute] public string ID { get; set; } [XmlAttribute] public string Title { get; set; } [XmlAttribute] public int Priority { get; set; } public Glyph Glyph { get; set; } } public class Glyph { [XmlAttribute] public int Collapsed { get; set; } [XmlAttribute] public int Expanded { get; set; } }
LItem節點也簡單,它就包含了URL,LinkGroup和一個文本節點,因此可以這樣定義它:
public class LItem { [XmlAttribute] public string URL { get; set; } [XmlAttribute] public string LinkGroup { get; set; } [XmlText] public string Title { get; set; } }
Context節點也不復雜,就只包含了一個LItem列表,因此可以這樣定義它:
public class Context { public List<LItem> Links { get; set; } }
好了,類型都定義好了,再來試試反序列化:
DynamicHelp help = XmlHelper.XmlDeserializeFromFile<DynamicHelp>("Links.xml", Encoding.UTF8); foreach( LinkGroup group in help.Groups ) Console.WriteLine("ID: {0}, Title: {1}, Priority: {2}, Collapsed: {3}, Expanded: {4}", group.ID, group.Title, group.Priority, group.Glyph.Collapsed, group.Glyph.Expanded); foreach( LItem item in help.Context.Links ) Console.WriteLine("URL: {0}, LinkGroup: {1}, Title: {2}", item.URL.Substring(0, 15), item.LinkGroup, item.Title);
屏幕顯示:
未處理的異常: System.InvalidOperationException: XML 文檔(4, 2)中有錯誤。 ---> System.InvalidOperationException: 不應有 <DynamicHelp xmlns=‘http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd‘>。
哦,拋異常了。
別急,看看異常說什麽。
好像是在說命名空間不能識別。
根據異常的描述,我還要修改一下DynamicHelp的定義,改成這樣:
[XmlRoot(Namespace = "http://msdn.microsoft.com/vsdata/xsd/vsdh.xsd")] public class DynamicHelp
再次運行,結果如下:
ID: sites, Title: Venus Sites, Priority: 1500, Collapsed: 3, Expanded: 4 ID: Venus Private Forums, Title: Venus Private Forums, Priority: 1400, Collapsed: 3, Expanded: 4 ID: ASP.NET Forums, Title: ASP.NET 1.0 Public Forums, Priority: 1200, Collapsed: 3, Expanded: 4 URL: http://www.asp., LinkGroup: sites, Title: Venus Home Page URL: http://www.asp., LinkGroup: sites, Title: ASP.NET Home Page URL: http://www.asp., LinkGroup: Venus Private Forums, Title: General Discussions URL: http://www.asp., LinkGroup: Venus Private Forums, Title: Feature Requests URL: http://www.asp., LinkGroup: Venus Private Forums, Title: Bug Reports URL: http://www.asp., LinkGroup: Venus Private Forums, Title: ASP.NET 2.0 Related issues URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Announcements URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Getting Started URL: http://www.asp., LinkGroup: ASP.NET Forums, Title: Web Forms
小結:根據XML結構推導類型時,要保證類型的層次結構與XML匹配, 數據的存放方式可以通過[XmlElement],[XmlAttribute],[XmlText]方式來指出。
回到頂部反序列化的使用總結
如果XML是由類型序列化得到那的,那麽反序列化的調用代碼是很簡單的,
反之,如果要面對一個沒有類型的XML,就需要我們先設計一個(或者一些)類型出來,
這是一個逆向推導的過程,請參考以下步驟:
1. 首先要分析整個XML結構,定義與之匹配的類型,
2. 如果XML結構有嵌套層次,則需要定義多個類型與之匹配,
3. 定義具體類型(一個層級下的XML結構)時,請參考以下表格。
XML形式 | 處理方法 | 補充說明 |
XmlElement | 定義一個屬性 | 屬性名與節點名字匹配 |
XmlAttribute | [XmlAttribute] 加到屬性上 | |
InnerText | [XmlText] 加到屬性上 | 一個類型只能使用一次 |
節點重命名 | 根節點:[XmlType("testClass")] 元素節點:[XmlElement("name")] 屬性節點:[XmlAttribute("id")] 列表子元素節點:[XmlArrayItem("Detail")] 列表元素自身:[XmlArray("Items")] |
排除不需要序列化的成員
默認情況下,類型的所有公開的數據成員(屬性,字段)在序列化時都會被輸出, 如果希望排除某些成員,可以用[XmlIgnore]來指出,例如:
public class TestIgnore { [XmlIgnore] // 這個屬性將不會參與序列化 public int IntValue { get; set; } public string StrValue { get; set; } public string Url; }
序列化調用代碼:
TestIgnore c1 = new TestIgnore { IntValue = 3, StrValue = "Fish Li" }; c1.Url = "http://www.cnblogs.com/fish-li/"; string xml = XmlHelper.XmlSerialize(c1, Encoding.UTF8); Console.WriteLine(xml);
輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <TestIgnore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Url>http://www.cnblogs.com/fish-li/</Url> <StrValue>Fish Li</StrValue> </TestIgnore>回到頂部
強制指定成員的序列化順序
前面的示例很奇怪,我明明先定義的StrValue,後定義的Url,可是在輸出時的順序並是我期望的。
如果你希望控制序列化的輸出順序,可以參考下面的示例代碼(註意紅色粗體文字):
public class TestIgnore { [XmlIgnore] // 這個屬性將不會參與序列化 public int IntValue { get; set; } [XmlElement(Order = 1)] public string StrValue { get; set; } [XmlElement(Order = 2)] public string Url; }
最終的輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <TestIgnore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <StrValue>Fish Li</StrValue> <Url>http://www.cnblogs.com/fish-li/</Url> </TestIgnore>回到頂部
自定義序列化行為
由於種種原因,可能需要我們自己控制序列化和反序列化的過程, 對於這種需求, .net framework也是支持的,下面我來演示如何這個過程。
假如我現在有這樣的類型定義:
public class TestClass { public string StrValue { get; set; } public List<int> List { get; set; } } public class ClassB1 { public TestClass Test { get; set; } }
測試代碼:
TestClass test = new TestClass { StrValue = "Fish Li", List = new List<int> { 1, 2, 3, 4, 5 } }; ClassB1 b1 = new ClassB1 { Test = test }; string xml = XmlHelper.XmlSerialize(b1, Encoding.UTF8); Console.WriteLine(xml); Console.WriteLine("-----------------------------------------------------"); ClassB1 b2 = XmlHelper.XmlDeserialize<ClassB1>(xml, Encoding.UTF8); Console.WriteLine("StrValue: " + b2.Test.StrValue); foreach( int n in b2.Test.List ) Console.WriteLine(n);
此時程序的輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <ClassB1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Test> <StrValue>Fish Li</StrValue> <List> <int>1</int> <int>2</int> <int>3</int> <int>4</int> <int>5</int> </List> </Test> </ClassB1> ----------------------------------------------------- StrValue: Fish Li 1 2 3 4 5
現在我可能會想:TestClass這個類太簡單了,但它輸出的XML長度復雜了點,能不能再短小一點,讓網絡傳輸地更快呢?
在這裏,我想到了自定義序列化行為來實現,請看下面對TestClass的重新定義。
public class TestClass : IXmlSerializable { public string StrValue { get; set; } public List<int> List { get; set; } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { StrValue = reader.GetAttribute("s"); string numbers = reader.ReadString(); if( string.IsNullOrEmpty(numbers) == false ) List = (from s in numbers.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries) let n = int.Parse(s) select n).ToList(); } public void WriteXml(XmlWriter writer) { writer.WriteAttributeString("s", StrValue); writer.WriteString(string.Join(",", List.ConvertAll<string>(x => x.ToString()).ToArray())); } }
繼續使用前面的測試代碼,現在的輸出結果如下:
<?xml version="1.0" encoding="utf-8"?> <ClassB1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Test s="Fish Li">1,2,3,4,5</Test> </ClassB1> ----------------------------------------------------- StrValue: Fish Li 1 2 3 4 5
很明顯,現在的序列化結果要比以前的結果小很多。
而且,測試代碼中的反序列化的顯示也表明,我們仍然可以通過反序列化來讀取它。
序列化去掉XML命名空間及聲明頭
在前面的示例中,我們會發現有時很簡單的XML在加了命名空間及聲明頭以後,結構變復雜了,內容也變長了。 有些人看到它們可能總是感覺非常別扭,例如:
<?xml version="1.0" encoding="utf-8"?> <ClassB1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Test s="Fish Li">1,2,3,4,5</Test> </ClassB1>
能不能只顯示成下面這樣呢?
<ClassB1> <Test s="Fish Li">1,2,3,4,5</Test> </ClassB1>
答案是肯定的,按下面的方法修改本文的示例代碼:
private static void XmlSerializeInternal(Stream stream, object o, Encoding encoding) { if( o == null ) throw new ArgumentNullException("o"); if( encoding == null ) throw new ArgumentNullException("encoding"); XmlSerializer serializer = new XmlSerializer(o.GetType()); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.NewLineChars = "\r\n"; settings.Encoding = encoding; settings.IndentChars = " "; // 不生成聲明頭 settings.OmitXmlDeclaration = true; // 強制指定命名空間,覆蓋默認的命名空間。 XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add(string.Empty, string.Empty); using( XmlWriter writer = XmlWriter.Create(stream, settings) ) { serializer.Serialize(writer, o, namespaces); writer.Close(); } }
說明:去掉XML命名空間及聲明頭不影響反序列化。
回到頂部XML的使用建議
在服務端,C#代碼中:
1. 建議不用使用低級別的XML API來使用XML,除非你是在設計框架或者通用類庫。
2. 建議使用序列化、反序列化的方法來生成或者讀取XML
3. 當需要考慮使用XML時,先不要想著XML結構,先應該定義好數據類型。
4. 列表節點不要使用[XmlElement],它會讓所有子節點【升級】,顯得結構混亂。
5. 如果希望序列化的XML長度小一點,可以采用[XmlAttribute],或者指定一個更短小的別名。
6. 不要在一個列表中輸出不同的數據類型,這樣的XML結構的可讀性不好。
7. 盡量使用UTF-8編碼,不要使用GB2312編碼。
在客戶端,JavaScript代碼中,我不建議使用XML,而是建議使用JSON來代替XML,因為:
1. XML文本的長度比JSON要長,會占用更多的網絡傳輸時間(畢竟數據保存在服務端,所以傳輸是免不了的)
2. 在JavaScritp中使用XML比較麻煩(還有瀏覽器的兼容問題),反而各種瀏覽器對JSON有非常好的支持。
在.net中序列化讀寫xml方法的總結