Xml序列化及其序列化工具的使用

    今天的主角是.net的Xml序列化。

    在主角出现前,先回想一下,平时什么地方用了Xml序列化吧:

    第一个想到的当然是Web Service和更进一步的WCF,没有Xml序列化的话,就需要手动处理Soap协议的各种输入和输出,其复杂性将会成倍的增长。

    第二个想到的就是Xml序列化其实就是一个Xml与对象之间的桥梁,可以把一个实例Xml变成一个实例对象,也可以把一个实例对象变成一个实例Xml,这在需要持久化的场合非常有用。

工具

    工欲善其事,必先利其器。首先来看看关于Xml序列化的工具吧。

    这些工具通常在X:\Program Files\Microsoft SDKs\Windows\v6.0A\bin目录下,其中和Xml序列关系比较大的有xsd.exe、wsdl.exe、svcutil.exe。当然其他工具在.net里面也是非常重要的,可以在这里察看所有工具的用途和使用方式。这里重点要用到的是xsd.exe。

    当然这个工具有三种用法,分别是:

  • Xml First,先有Xml实例,适合先想好Xml是什么样,或者已经有Xml实例的情况
  • Xsd First,先有Xsd,适合于可以获得Xsd,或者熟悉Xsd的人,并且对Xml有很强的控制欲的人(某人飘过)
  • Class First,先有c#类型,适合于先有c#代码的情况

    接下来将分别介绍这3种方式的。

Xml First

    这种情况首先有一个Xml实例,例如:

<?xml version="1.0" encoding="utf-8" ?>
<persons>
  <person name="Zhenway, Yan">
    <goodat>Xml</goodat>
    <goodat>Reflection</goodat>
  </person>
  <person name="Allen, Lee">
    <goodat>Ruby</goodat>
    <goodat>F#</goodat>
    <goodat>Windows Mobile</goodat>
    <goodat>Linq</goodat>
  </person>
</persons>

    利用Xsd命令:“xsd XmlFirst.xml”,就可以根据这个实例获得xsd(当然不会是非常精确的,但基本上能用):

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="persons" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="persons" msdata:IsDataSet="true" msdata:Locale="en-US">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="person">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="goodat" nillable="true" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:simpleContent msdata:ColumnName="goodat_Text" msdata:Ordinal="0">
                    <xs:extension base="xs:string">
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" />
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

    当然,如果对这个Xsd不太满意的话,还可以修改一下。这样就把Xml First转换成Xsd First。

Xsd First

    这里首先需要一个Xsd(某个控制欲极强的人重新写了一下xsd)

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="persons" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element name="goodat" type="xs:string" minOccurs="1" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:string"/>
  </xs:complexType>
  <xs:complexType name="Persons">
    <xs:sequence>
      <xs:element name="person" type="Person" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="persons" type="Persons"/>
</xs:schema>

    这里要求每个person的goodat至少要有一项。

    然后利用xsd命令:“xsd XsdFirst.xsd /c”,这样就可以获得一个cs文件,整理后,如下:

using System;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Schema;
using System.Xml.Serialization;

[GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
[SerializableAttribute()]
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
[XmlRootAttribute("persons", Namespace = "", IsNullable = false)]
public partial class Persons
{
    private Person[] personField;

    [System.Xml.Serialization.XmlElementAttribute("person", Form = XmlSchemaForm.Unqualified)]
    public Person[] person
    {
        get { return this.personField; }
        set { this.personField = value; }
    }
}

[GeneratedCodeAttribute("xsd", "2.0.50727.1432")]
[SerializableAttribute()]
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
public partial class Person
{
    private string[] goodatField;

    private string nameField;

    [XmlElementAttribute("goodat", Form = XmlSchemaForm.Unqualified)]
    public string[] goodat
    {
        get { return this.goodatField; }
        set { this.goodatField = value; }
    }

    [XmlAttributeAttribute()]
    public string name
    {
        get { return this.nameField; }
        set { this.nameField = value; }
    }
}

    这样就可以获得一个类型与这个Xsd对应,在对象实例与这个Xsd实例之间建立一座桥梁。

Class First

    这种情况适合于先有类型,然后想持久化的情况,例如拥有一个下列的类型:

[XmlRoot("persons")]
public class ClassFirst
{
    [XmlElement("person")]
    public Person[] PersonCollection { get; set; }
}

public class Person
{
    [XmlAttribute("name")]
    public string Name { get; set; }
    [XmlElement("goodat")]
    public string[] GoodAt { get; set; }
}

    Build以后,执行命令:“xsd ClassFirstSample.dll”,就可以获得这样一个xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="persons" nillable="true" type="ClassFirst" />
  <xs:complexType name="ClassFirst">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="person" type="Person" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="goodat" type="xs:string" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" />
  </xs:complexType>
  <xs:element name="Person" nillable="true" type="Person" />
</xs:schema>

    这个xsd就是序列化出来的xml的Schema,基本上与。

XmlSerializer

    前面的三种方式都只是在OOP与Xml之间建立一个契约,现在来介绍这个桥梁——XmlSerializer

    废话就不说了,先来看看如何把对象转换成xml:

XmlSerializer serializer = new XmlSerializer(typeof(Persons));
serializer.Serialize(Console.Out, new Persons
{
    person = new Person[]
    {
        new Person
        {
            name = "Zhenway, Yan",
            goodat = new string[] { "Reflection", "Xml" },
        },
        new Person
        {
            name = "Allen, Lee",
            goodat = new string[] { "Ruby", "F#", "Windows Mobile", "Linq" },
        },
    }
});

    来看看输出:

image

    很好,接下来看看如何反过来,从Xml获得对象实例:

XmlSerializer serializer = new XmlSerializer(typeof(Persons));
using (var reader = File.OpenText("XmlFirst.xml"))
{
    Persons ps = (Persons)serializer.Deserialize(reader);
    foreach (var p in ps.person)
    {
        Console.WriteLine("name='{0}', good at={1}",
            p.name, string.Join(", ", p.goodat));
    }
}

    看看输出:

image

    不过需要注意的一点是,如果需要反序列化对象的话,需要对临时目录(根据Windows的TEMP环境变量定义)的写入权限。

    另外,XmlSerializer会自动生成一个Assembly用于加速序列化和反序列化,不过要注意由于AppDomain无法单独卸载一个Assembly的特性,所以当产生过多的Assembly时,就会导致内存占用过多。

    尽管XmlSerializer也会尽量利用现有的Assembly,不过这仅仅发生在(Type)构造函数,和(Type,String)构造函数时才会发生,而其他构造函数将再次创建Assembly,如果放置在循环中,这样将导致AppDomain中Assembly数量激增,因此缓存XmlSerializer在某些场合下是非常必要的。

    另外msdn上对XmlSerializer的描述有一处非常特别的地方,“此类型是线程安全的”,这在整个msdn中并不多见,也就是说,不用对缓存的XmlSerializer对象做任何的同步处理。

 

更多控制

    XmlSerializer本身支持很多扩展,其中包括使用属性控制 XML 序列化,和更加可定制化的IXmlSerializable接口,这里限于篇幅就省略相关的内容。

posted @ 2012-03-28 22:55  初雪之恋  阅读(1469)  评论(0编辑  收藏  举报