这篇随笔对应的.Net命名空间是System.Xml.Serialization;文中的示例代码需要引用这个命名空间。
为什么要做序列化和反序列化?
.Net程序执行时,对象都驻留在内存中;内存中的对象如果需要传递给其他系统使用;或者在关机时需要保存下来以便下次再次启动程序使用就需要序列化和反序列化。
范围:本文只介绍xml序列化,其实序列化可以是二进制的序列化,也可以是其他格式的序列化。
看一段最简单的Xml序列化代码
01 class Program
02 {
03     static void Main(string[] args)
04     {
05         int i = 10;
06         //声明Xml序列化对象实例serializer
07         XmlSerializer serializer = new XmlSerializer(typeof(int));
08         //执行序列化并将序列化结果输出到控制台
09         serializer.Serialize(Console.Out, i);
10         Console.Read();
11     }
12 }

上面代码对int i进行了序列化,并将序列化的结果输出到了控制台,输出结果如下

<?xml version="1.0" encoding="gb2312"?>
<int>10</int>

可以将上述序列化的xml进行反序列化,如下代码

01 static void Main(string[] args)
02 {
03     using (StringReader rdr = new StringReader(@"<?xml version=""1.0"" encoding=""gb2312""?>
04 <int>10</int>"))
05     {
06         //声明序列化对象实例serializer
07         XmlSerializer serializer = new XmlSerializer(typeof(int));
08         //反序列化,并将反序列化结果值赋给变量i
09         int i = (int)serializer.Deserialize(rdr);
10         //输出反序列化结果
11         Console.WriteLine("i = " + i);
12         Console.Read();
13     }
14 }

以上代码用最简单的方式说明了xml序列化和反序列化的过程,.Net系统类库为我们做了大量的工作,序列化和反序列化都非常简单。但是在现实中业务需求往往比较复杂,不可能只简单的序列化一个int变量,显示中我们需要对复杂类型进行可控制的序列化。

自定义对象的Xml序列化:

System.Xml.Serialization命名空间中有一系列的特性类,用来控制复杂类型序列化的控制。例如 XmlElementAttribute、XmlAttributeAttribute、XmlArrayAttribute、 XmlArrayItemAttribute、XmlRootAttribute等等。

看一个小例子,有一个自定义类Cat,Cat类有三个属性分别为Color,Saying,Speed。

01 namespace UseXmlSerialization
02 {
03     class Program
04     {
05         static void Main(string[] args)
06         {
07             //声明一个猫咪对象
08             var c = new Cat { Color = "White", Speed = 10, Saying = "White or black,  so long as the cat can catch mice,  it is a good cat" };
09  
10             //序列化这个对象
11             XmlSerializer serializer = new XmlSerializer(typeof(Cat));
12  
13             //将对象序列化输出到控制台
14             serializer.Serialize(Console.Out, c);
15  
16             Console.Read();
17         }
18     }
19  
20     [XmlRoot("cat")]
21     public class Cat
22     {
23         //定义Color属性的序列化为cat节点的属性
24         [XmlAttribute("color")]
25         public string Color { get; set; }
26  
27         //要求不序列化Speed属性
28         [XmlIgnore]
29         public int Speed { get; set; }
30  
31         //设置Saying属性序列化为Xml子元素
32         [XmlElement("saying")]
33         public string Saying { get; set; }
34     }
35 }

可以使用XmlElement指定属性序列化为子节点(默认情况会序列化为子节点);或者使用XmlAttribute特性制定属性序列化为Xml节点的属性;还可以通过XmlIgnore特性修饰要求序列化程序不序列化修饰属性。

对象数组的Xml序列化:

数组的Xml序列化需要使用XmlArrayAttribute和XmlArrayItemAttribute;XmlArrayAttribute指定数组元素的Xml节点名,XmlArrayItemAttribute指定数组元素的Xml节点名。

如下代码示例:

01 /*玉开技术博客 javascript:void(0) */
02 using System;
03 using System.Collections.Generic;
04 using System.Linq;
05 using System.Text;
06 using System.Xml.Serialization;
07  
08 namespace UseXmlSerialization
09 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             //声明一个猫咪对象
15             var cWhite = new Cat { Color = "White", Speed = 10, Saying = "White or black,  so long as the cat can catch mice,  it is a good cat" };
16             var cBlack = new Cat { Color = "Black", Speed = 10, Saying = "White or black,  so long as the cat can catch mice,  it is a good cat" };
17  
18             CatCollection cc = new CatCollection { Cats = new Cat[] { cWhite,cBlack} };
19  
20             //序列化这个对象
21             XmlSerializer serializer = new XmlSerializer(typeof(CatCollection));
22  
23             //将对象序列化输出到控制台
24             serializer.Serialize(Console.Out, cc);
25  
26             Console.Read();
27         }
28     }
29  
30     [XmlRoot("cats")]
31     public class CatCollection
32     {
33         [XmlArray("items"),XmlArrayItem("item")]
34         public Cat[] Cats { get; set; }
35     }
36  
37     [XmlRoot("cat")]
38     public class Cat
39     {
40         //定义Color属性的序列化为cat节点的属性
41         [XmlAttribute("color")]
42         public string Color { get; set; }
43  
44         //要求不序列化Speed属性
45         [XmlIgnore]
46         public int Speed { get; set; }
47  
48         //设置Saying属性序列化为Xml子元素
49         [XmlElement("saying")]
50         public string Saying { get; set; }
51     }
52 }

以上代码将输出:

01 <?xml version="1.0" encoding="gb2312"?>
02 <cats xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://ww
03 w.w3.org/2001/XMLSchema">
04   <items>
05     <item color="White">
06       <saying>White or black,  so long as the cat can catch mice,  it is a good
07 cat</saying>
08     </item>
09     <item color="Black">
10       <saying>White or black,  so long as the cat can catch mice,  it is a good
11 cat</saying>
12     </item>
13   </items>
14 </cats>

XmlSerializer内存泄漏问题:

多谢chenlulouis,仔细看了下msdn,确实存在泄漏的情况,msdn说明如下:

动态生成的程序集

为了提高性能,XML 序列化基础结构将动态生成程序集,以序列化和反序列化指定类型。此基础结构将查找并重复使用这些程序集。此行为仅在使用以下构造函数时发生:

XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)

如果使用任何其他构造函数,则会生成同一程序集的多个版本,且绝不会被卸载,这将导致内存泄漏和性能降低。最简单的解决方案是使用先前提到的两个构造函数的其中一个。否则,必须在 Hashtable 中缓存程序集,如以下示例中所示。

也就是说我们在使用XmlSerializer序列化,初始化XmlSerializer对象时最好使用下面两个构造函数否则会引起内存泄漏。
XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)

C#处理Xml相关随笔

4.通过XmlScheme定义固定格式xml文档
6.通过XPath查找Xml节点
7.通过Xslt转化Xml格式