xml对象的序列化和反序列化

XmlSerializer 对象的Xml序列化和反序列化

这篇随笔对应的.Net命名空间是System.Xml.Serialization;文中的示例代码需要引用这个命名空间。

为什么要做序列化和反序列化?

.Net程序执行时,对象都驻留在内存中;内存中的对象如果需要传递给其他系统使用;或者在关机时需要保存下来以便下次再次启动程序使用就需要序列化和反序列化。

范围:本文只介绍xml序列化,其实序列化可以是二进制的序列化,也可以是其他格式的序列化。

看一段最简单的Xml序列化代码

1

2

3

4

5

6

7

8

9

10

11

12

class Program

{

    static void Main(string[] args)

    {

        int i = 10;

        //声明Xml序列化对象实例serializer

        XmlSerializer serializer = new XmlSerializer(typeof(int));

        //执行序列化并将序列化结果输出到控制台

        serializer.Serialize(Console.Out, i);

        Console.Read();

    }

}

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

<?xml version="1.0" encoding="gb2312"?>

<int>10</int>

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

static void Main(string[] args)

{

    using (StringReader rdr = new StringReader(@"<?xml version=""1.0"" encoding=""gb2312""?>

<int>10</int>"))

    {

        //声明序列化对象实例serializer

        XmlSerializer serializer = new XmlSerializer(typeof(int));

        //反序列化,并将反序列化结果值赋给变量i

        int i = (int)serializer.Deserialize(rdr);

        //输出反序列化结果

        Console.WriteLine("i = " + i);

        Console.Read();

    }

}

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

自定义对象的Xml序列化:

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

namespace UseXmlSerialization

{

    class Program

    {

        static void Main(string[] args)

        {

            //声明一个猫咪对象

            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" };

 

            //序列化这个对象

            XmlSerializer serializer = new XmlSerializer(typeof(Cat));

 

            //将对象序列化输出到控制台

            serializer.Serialize(Console.Out, c);

 

            Console.Read();

        }

    }

 

    [XmlRoot("cat")]

    public class Cat

    {

        //定义Color属性的序列化为cat节点的属性

        [XmlAttribute("color")]

        public string Color { getset; }

 

        //要求不序列化Speed属性

        [XmlIgnore]

        public int Speed { getset; }

 

        //设置Saying属性序列化为Xml子元素

        [XmlElement("saying")]

        public string Saying { getset; }

    }

}

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

 

对象数组的Xml序列化:

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

如下代码示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

/*玉开技术博客 http://www.cnblogs.com/yukaizhao */

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml.Serialization;

 

namespace UseXmlSerialization

{

    class Program

    {

        static void Main(string[] args)

        {

            //声明一个猫咪对象

            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" };

            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" };

 

            CatCollection cc = new CatCollection { Cats = new Cat[] { cWhite,cBlack} };

 

            //序列化这个对象

            XmlSerializer serializer = new XmlSerializer(typeof(CatCollection));

 

            //将对象序列化输出到控制台

            serializer.Serialize(Console.Out, cc);

 

            Console.Read();

        }

    }

 

    [XmlRoot("cats")]

    public class CatCollection

    {

        [XmlArray("items"),XmlArrayItem("item")]

        public Cat[] Cats { getset; }

    }

 

    [XmlRoot("cat")]

    public class Cat

    {

        //定义Color属性的序列化为cat节点的属性

        [XmlAttribute("color")]

        public string Color { getset; }

 

        //要求不序列化Speed属性

        [XmlIgnore]

        public int Speed { getset; }

 

        //设置Saying属性序列化为Xml子元素

        [XmlElement("saying")]

        public string Saying { getset; }

    }

}

以上代码将输出:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<?xml version="1.0" encoding="gb2312"?>

<cats xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://ww

w.w3.org/2001/XMLSchema">

  <items>

    <item color="White">

      <saying>White or black,  so long as the cat can catch mice,  it is a good

cat</saying>

    </item>

    <item color="Black">

      <saying>White or black,  so long as the cat can catch mice,  it is a good

cat</saying>

    </item>

  </items>

</cats>

XmlSerializer内存泄漏问题:

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

动态生成的程序集 

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

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

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

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

不错,之前研究了XML文件转类,现在看到了LZ的类-->XML,不错,学习了,继续加油.

支持(0)反对(0)

  

#2楼 2011-07-22 09:56 | 首席技术官  

1

2

3

4

5

6

7

8

9

10

<?xml version="1.0" encoding="gb2312"?>

<cats xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://ww

    w.w3.org/2001/XMLSchema">

    <cat color="White">

      <saying>White or black,  so long as the cat can catch mice,  it is a good cat</saying>

    </cat>

    <cat color="Black">

      <saying>White or black,  so long as the cat can catch mice,  it is a good cat</saying>

    </cat>

</cats>


这样会不会更好?

支持(0)反对(0)

  

#3楼 2011-07-22 09:58 | 首席技术官  

1

2

3

4

5

6

[XmlRoot("cats")]

public class CatCollection

{

    [XmlElement("cat")]

    public Cat[] Cats { getset; }

}

支持(0)反对(0)

  

#4楼[楼主] 2011-07-22 10:11 | 玉开  

@ 首席技术官
这样生成的xml确实更简洁,谢谢提示。

我文中的例子是展示XmlArrayAttribute和XmlArrayItemAttribute用法的,所以生成的xml就是那个样子了。不管怎样,谢谢。

支持(0)反对(0)

  

#5楼 2011-07-22 11:09 | 陈-chen  

很好,浅显易懂!

支持(0)反对(0)

  

#6楼 2011-07-22 11:10 | chenlulouis  

XmlSerializer 要慎用, 容易引起动态加载dll泄漏

支持(0)反对(0)

  

#7楼[楼主] 2011-07-22 11:20 | 玉开  

引用chenlulouis:XmlSerializer 要慎用, 容易引起动态加载dll泄漏
我知道XmlSerializer生成一个dll,但是从来没有遇到过你说的问题,可否详细说明?

支持(0)反对(0)

  

#8楼 2011-07-22 11:32 | chenlulouis  

印象中,每次调用 xmlserialzer 构造的时候,应用程序域都会动态加载序列化类型的dll, 这样长期运行会加载很多dll而不会卸载(除非应用程序欲卸载),最终结果是 你托管堆和GC 都很小 但是内存很大 都被DLL占用了

支持(0)反对(0)

  

#9楼[楼主] 2011-07-22 12:49 | 玉开  

@ chenlulouis
按你这么说那是一个大bug呀,xml序列化从.net1就开始有了,微软不会让这个bug一直存在吧?

支持(0)反对(0)

  

#10楼 2011-07-22 13:56 | chenlulouis  

http://msdn.microsoft.com/zh-cn/library/system.xml.serialization.xmlserializer%28VS.95%29.aspx
这里有关于泄漏的说明

支持(0)反对(0)

  

#11楼 2011-07-22 14:16 | 东华一只球  

引用首席技术官:[code=html]
<?xml version="1.0" encoding="gb2312"?>
<cats xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://ww
w.w3.org/2001/XMLSchema">
<cat color="White">
<saying>White or black, so long as the cat can catch mice, it is a good cat</saying>
</cat>
...
代码非常好看,优化不错

支持(0)反对(0)

  

#12楼[楼主] 2011-07-22 15:25 | 玉开  

引用chenlulouis:http://msdn.microsoft.com/zh-cn/library/system.xml.serialization.xmlserializer%28VS.95%29.aspx
这里有关于泄漏的说明


谢谢你,chenlulouis,仔细看了下msdn,确实存在泄漏的情况,msdn说明如下:
[I]
动态生成的程序集

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

XmlSerializer(Type)

XmlSerializer.XmlSerializer(Type, String)

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

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

XmlSerializer.XmlSerializer(Type, String)

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值