xml 是设计用于在各种不同的系统之间进行数据交换的技术。您可以轻松地在分布式组件之间传送xml,这归功于他的平台独立性、简介、基于文本、自描述格式,然而这些却很难构成可靠编程平台的基础。基于文本的数据没有强类型安全规则。程序员往往易受面向对象模式的诱惑,因为每个对象都属于某一类型,因为编译器可警惕潜在的类型问题,对象中封装的数据可以轻松的访问到。理想的编程环境是用面向对象的模型构建软件,但是充分利用xml在分布式组件(如在Internet 或 Messae Queue 中)之间优势。这就是xml串行化之所以重要的原因:它提供了一座桥梁,使您能够不露痕迹地将对象转换为xml,反之亦然。
xml串行化是将数据的集合转换为信息流的过程。反串行化则是相反的过程:将信息流转换回原先生成该信息流的数据。有时将这两个相逆的过程称为脱水(dehydration)和水合(rehydration)。
下面将详细介绍以下内容:
1.xml串行化
2.如何将对象串行化为xml格式
3.用XmlSerializer类串行化对象图
4.用设计时间属性自定义串行化输出
5.在xml串行化期间处理命名空间
6.如何将xml表示反串行化为对象
7.用XmlSerializer类串行化和反串行化通用类型
8.通过使用新增的xml Serializer Gdnerator 工具改进串行化性能
串行化入门
串行化是运行时进程,将对象或对象的图转换为线性序列字节,然后可以将合成的内存块用于存储或通过特定协议在网络间传输。在.net framework 中,对象串行化可以有3种不同的输出格式:
Binary (二进制) -------- 将对象格式化为二进制
Simple Object Access Protocol (SOAP) ---------- 将对象格式化为SOAP格式
XML ------------ 将对象格式化为XML格式,从而可以传输到另一种应用程序中
运行时对象串行化(二进制和SOAP) 和 XML格式是非常不同的两种技术,不仅实现方式不同,更重要的目标不同。尽管如此,这两种形式都完成同一关键的事情:保存内容、内存外的活动对象,并从内存转移到其他任何存储介质。
注意:
System.Runtime.Serialization 命名空间中包含的格式化程序命名空间和类支持二进制和SOAP串行化。XML串行化主要由Sytem.Xml.Serialization命名空间中的XmlSerializer类支持。
串行化对于用简单的格式传输或存储复杂的数据很有用,例如,应用程序可以构建包含几十个对象的复杂数据结构,并将它串行化为文本字符串。然后可以通过网络将字符串传送到另一个程序。接受程序将反串行化文本,从而构建一个原始数据结构的副本。串行化不必将对象转换为文本。它可以将对象表示为二进制数据流。它也不必通过网络传送数据。串行化的这些用途有一个共同的特点:将可能非常复杂的数据通信转换为简单的串行表示,然后转换回来。
1.1 XmlSerializer
XmlSerializer.Serialize()方法的第一个参数是重写的,因此可以将XML串行化为Stream, TextWriter, XmlWriter
XmlSerializer 类在Sytem.Xml.Serialization命名空间的详细信息自己查询msdn,在这里不介绍了。
1.串行化对象
首先是创建要通过XmlSerializer 类串行化的类
程序清单12-1
接下来就可以串行化该类并将其转换为xml格式。
程序清单12-2
打开Category.xml文件
Category类所有公有字段都串行化到xml元素中,其名称和类声明中的字段名相同。所有这些元素包含在根目录Category 元素中,该元素映射回类的名称。
2.处理对象图
同样的机制也可以用来串行化相关对象的整个图。例如Category类可以将所有产品(属于同样的类别)封装为一个嵌套元素。
与Category类相似,product类也由一些公有属性组成。
把下面这行代码添加到Category类中。
public Product[] products;
现在,您想串行化Category类,那么也会产生要串行化的Products数组。
程序清单12-5
打开Category.xml文件
高级串行化
在前面几个例子中,类的公有字段直接映射到输出xml文档的xml元素中。虽然这个一对一的映射方法对简单串行化场景有效,然而有时候却需要您自定义生成的输出。幸运的是,xml串行化使您可以自定义要创建的xml数据的最终的输出结果。
1.xml串行化属性(Attrbute)
XmlAttributes类表示.net framework 属性的集合,这个集合使您能够联系XmlSerializer类如何处理对象的完整控件
说明:
XmlAttributes类与SoapAttributes类相似,只有一点不同:XmlAttributes类的输出为xml格式,而SoapAttributes类返回带类型信息的SOAP编码消息。
XmlAttributes类的每个属性都对应于一个属性类。
xml串行化的一个好处是,它使您能够控制要生成的xml文档的结构。您可以将特殊属性(上表)应用到这个类的成员来完成该功能。下面显示了带xml串行化属性的Category类。
对Category类做了这些修改后,如果您从浏览器请求程序清单12-2中所示的aspx页时,打开生成的Category.xml应看到下面代码:
正如您所看到的,已经用XmlRoot属性类将根元素重命名为CategoryRoot。作为XmlRoot属性的一部分,您也可以为Namespace 和 IsNullable 属性指定值。然后,CategoryID、CategoryName 元素将被分别重命名为ID、Name,这两者也将作为属性创建。
2.用XmlAttributeOverride控制输出
前面介绍了如何用硬编码的xml串行化属性的方式来重写xml元素。
说明:
在运行时重写xml元素的功能是非常有用的,可以是很多场景有用。设想将应用程序的目录更新内容用xml格式发送到感兴趣的聚会上。由于某些原因,有一个客户需要一种略微不同的格式。您可以用XmlAttributeOverrides类简单地自定义现有这套类即可,而不用写一整套不同的类来生成自定义格式。
就本例而言,使用清单12-1显示的Category类。但是在串行化时,您将在Category类将CategoryID字段重命名为ID,同时将其作为xml属性添加,而不是作为xml元素添加。
XmlAttrite 对象收集所有想加入给定元素的重写。在这种情况下,新建一个XmlAttributeAttribute对象后,可以修改属性名称,并将结果对象存储在重写容器的XmlAttribute属性中。
程序清单 12-8
打开生成的Category.xml应看到下面代码:
注意:
对于每个重写对象,需要一个独特的Xmlattribute对象。这就意味着如果试图重写两个元素,则需要创建两个不同的Xmlattribute对象,并将其体那家到XmlAttributeOverrides对象中。
3.用命名空间生成受限的名称
通常,应用程序需要将新信息添加到已存在的xml文档中,或者已有的xml文档组合起来。为了避免在这些场合中的冲突,WC3联盟标准化了xml命名空间。您可以将命名空间看作元素和属性的最后一个名称。
添加到由XmlSerializer生成的xml中的默认命名空间是xmlns:xsd="http://www.w3.org/2001/XMLSchema" 和 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 。通过创建XmlSerializerNamespaces对象并用一列命名空间和别名可以对默认的命名空间进行重写。
使用Category类
程序清单12-9 使用XmlSerializerNamespaces生成受限的名称
删除xsd 和 xsi 声明
如果您以前曾经做过任何串行化,那么您一定会知道:当用XmlSerializer串行化类时将获得若干声明,这些声明作为结果xml的一部份,类似下面的输出:
<Category xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
正如您所见,xsd 和 xsi 命名空间声明被串行器放在输出结果中。要删除xsd 和 xsi 命名空间,需要创建一个空的XmlSerializerNamespaces类,并添加一个简单条目,指定一个空的命名空间前缀和命名空间url。代码如下:
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
4.串行化集合
集合类与数组类似,但是其长度不需要固定,可以容纳非相关的类型,是不同使用场景的最佳选择。.NET Framework 提供了大量用于System.Collection命名空间的集合,如ArrayList、Dictionary、或Hashtable 等只是其中一部分。
您也可以串行化多个对象的图。要串行化集合或数组,也必须向XmlSerializer对象提供关于集合的类型和其中的内容的一个或几个类型。例如要串行化的对象是ArrayList而其中的内容是Category和Product对象时。XmlSerializer类为这样的可能性准备了一个构造函数----它期待包含类的类型号,以及描述一些类的类型数组:
然后可以创建一个流,并和以前一样串行化该流。
串行化自定义集合
XmlSerializer也可以处理自定义集合对象,只要这些对象实现这两个.NET Framework 的集合接口之一:IEnumerable 或 ICollection 。.NET Framework提供的所有集合将实现这些接口,因此您可以串行化或反串行化这些集合,而不必增加额外工作。
说明:
只要符合一些条件,像集合(实现ICollection接口的对象)这样的容器对象的内容将被自动串行化;Add方法必须采用简单的正行参数。除了选择适当的对象类型外,当然,您必须确保集合的内容本身符合xml串行化需求,需求如下:每个类必须提供一个默认的构造函数,您应该提供一种通过可用的公开成员或属性访问类中的方式。
程序清单12-11 串行化CategoriesList对象
产生的xml结果
1.3 反串行化xml
要反串行化文件Category.xml(在前面的示例中创建的)中的Category对象,您可以直接打开文件流、初始XmlSerializer,并调用Deserialize。
程序清单12-12 将xml文件反串行化为对象
5.处理XmlSerializer引发的事件
如果输入流不符合预期的形式,那么反串行化过程将试图尽最大能力恢复,但是当过程完成时,作为结果的一个或多个对象可以设置为空值,为了帮助处理这些情况,XmlSerializer类发布了4个您可以遇到的事件。
XmlSerializer类的事件
UnknownAttribute 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 属性 (Attribute) 时发生。
UnknownElement 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 元素时发生。
UnknownNode 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 节点时发生。
UnreferencedObject 在反序列化 SOAP 编码的 XML 流的过程中发生,此时 XmlSerializer 遇到未使用(或未引用)的识别类型。
通过创建适当的委托,可捕获这些事件。
程序清单12-13 处理由XmlSerializer类引发的事件
该代码假定Category类的声明如下。
public class Category
{
public long CategoryID;
public string CategoryName;
}
正如前面所见,前面示例中用到的Description字段在Category类中消失了。作为程序清单12-13中的xml输入文档使用的xml文件内容如下:
输出结果如下:
Unknown Element:
Unknown Element Name: Description
Unknown Element Value: Soft drinks, coffees, teas, beers, and ales
Result of Deserialization:
CategoryID: 1
Category Name: Beverages
注意:XmlElementEventArgs对象提供了名为ObjectBeingDeserialized的属性,使您可以在反串行化期间引用Category对象。当您想执行一些基于对象将填充的内容时,非常有用。
6.用反串行化映射SQL Server 数据
xml 文档
程序清单12-4 Contace类
程序清单12-5 使用Contace对象将Contact数据映射到AdventureWorks数据库中
输出结果:
Unknown Element:
Unknown Element Name: EmailAddress
Unknown Element Value: catherine0@adventure-works.com
Result of Deserialization:
ID: 2
First Name: Catherine
Middle Name: R.
Last Name: Abel
1.4 泛型和XML串行化
CLR使用.NET Framwork2.0 大大增强表达力,泛型类型可增加对运行时的完全支持。xml串行化已延伸到串行化和反串行化的通用类型。
现分析一下泛型类型的代码,见程序清单12-16
程序清单12-17 用泛型执行串行化和反串行化
串行化泛型集合
除了创建简单的泛型类型外,您还可以创建强类型化泛型集合,这些集合的类型安全和性能比非泛型的强类型化集合更好。System.Collection.Generic命名空间包含若干用于定义泛型集合的接口和类。看一下下列代码,理解如何用List类创建强类型化的类别集合对象。
List<Category> list = new List<Category>();
程序清单12-18 Serializing Typed Generics Collections