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
using
System;
[Serializable]
public
class
Category
{
public long CategoryID;
public string CategoryName;
public string Description;
}
接下来就可以串行化该类并将其转换为xml格式。
程序清单12-2
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Category.xml";
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
XmlSerializer serializer = new XmlSerializer(typeof(Category));
TextWriter writer = new StreamWriter(xmlFilePath);
//Serialize the Category and close the TextWriter
serializer.Serialize(writer, categoryObj);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Simple XML Serialization
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
打开Category.xml文件
<?
xml version="1.0" encoding="utf-8"
?>
<
Category
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
>
<
CategoryID
>
1
</
CategoryID
>
<
CategoryName
>
Beverages
</
CategoryName
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
</
Category
>
Category类所有公有字段都串行化到xml元素中,其名称和类声明中的字段名相同。所有这些元素包含在根目录Category 元素中,该元素映射回类的名称。
2.处理对象图
同样的机制也可以用来串行化相关对象的整个图。例如Category类可以将所有产品(属于同样的类别)封装为一个嵌套元素。
using
System;
public
class
Product
{
public long ProductID;
public string ProductName;
public string QuantityPerUnit;
public string UnitPrice;
public int UnitsInStock;
}
与Category类相似,product类也由一些公有属性组成。
把下面这行代码添加到Category类中。
public Product[] products;
using
System;
[Serializable]
public
class
Category
{
public long CategoryID;
public string CategoryName;
public string Description;
public Product[] Products;
}
现在,您想串行化Category类,那么也会产生要串行化的Products数组。
程序清单12-5
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Category.xml";
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
//Populate the products array
Product prodObj = new Product();
prodObj.ProductID = 1;
prodObj.ProductName = "Chai";
prodObj.QuantityPerUnit = "10 boxes x 20 bags";
prodObj.UnitPrice = "18";
prodObj.UnitsInStock = 39;
//Insert the item into the array.
Product[] products = { prodObj };
categoryObj.Products = products;
XmlSerializer serializer = new XmlSerializer(typeof(Category));
TextWriter writer = new StreamWriter(xmlFilePath);
// Serialize the Category and close the TextWriter
serializer.Serialize(writer, categoryObj);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Nesting Objects during XML Serialization
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
打开Category.xml文件
<?
xml version="1.0" encoding="utf-8"
?>
<
Category
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
>
<
CategoryID
>
1
</
CategoryID
>
<
CategoryName
>
Beverages
</
CategoryName
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
<
Products
>
<
Product
>
<
ProductID
>
1
</
ProductID
>
<
ProductName
>
Chai
</
ProductName
>
<
QuantityPerUnit
>
10 boxes x 20 bags
</
QuantityPerUnit
>
<
UnitPrice
>
18
</
UnitPrice
>
<
UnitsInStock
>
39
</
UnitsInStock
>
</
Product
>
</
Products
>
</
Category
>
高级串行化
在前面几个例子中,类的公有字段直接映射到输出xml文档的xml元素中。虽然这个一对一的映射方法对简单串行化场景有效,然而有时候却需要您自定义生成的输出。幸运的是,xml串行化使您可以自定义要创建的xml数据的最终的输出结果。
1.xml串行化属性(Attrbute)
XmlAttributes类表示.net framework 属性的集合,这个集合使您能够联系XmlSerializer类如何处理对象的完整控件
说明:
XmlAttributes类与SoapAttributes类相似,只有一点不同:XmlAttributes类的输出为xml格式,而SoapAttributes类返回带类型信息的SOAP编码消息。
XmlAttributes类的每个属性都对应于一个属性类。
namespace
System.Xml.Serialization
{
// 摘要:
// 表示一个属性对象的集合,这些对象控制 System.Xml.Serialization.XmlSerializer 如何序列化和反序列化对象。
public class XmlAttributes
{
// 摘要:
// 初始化 System.Xml.Serialization.XmlAttributes 类的新实例。
public XmlAttributes();
//
// 摘要:
// 初始化 System.Xml.Serialization.XmlAttributes 类的新实例,并自定义 System.Xml.Serialization.XmlSerializer
// 序列化和反序列化对象的方式。
//
// 参数:
// provider:
// 能提供控制 XML 序列化的属性的其他实现的类。
public XmlAttributes(ICustomAttributeProvider provider);
// 摘要:
// 获取或设置要重写的 System.Xml.Serialization.XmlAnyAttributeAttribute。
//
// 返回结果:
// 要重写的 System.Xml.Serialization.XmlAnyAttributeAttribute。
public XmlAnyAttributeAttribute XmlAnyAttribute { get; set; }
//
// 摘要:
// 获取要重写的 System.Xml.Serialization.XmlAnyElementAttribute 对象集合。
//
// 返回结果:
// 表示 System.Xml.Serialization.XmlAnyElementAttribute 对象集合的 System.Xml.Serialization.XmlAnyElementAttributes
// 对象。
public XmlAnyElementAttributes XmlAnyElements { get; }
//
// 摘要:
// 获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何序列化返回数组的公共字段或读/写属性。
//
// 返回结果:
// System.Xml.Serialization.XmlArrayAttribute,指定 System.Xml.Serialization.XmlSerializer
// 序列化返回数组的公共字段或读/写属性的方式。
public XmlArrayAttribute XmlArray { get; set; }
//
// 摘要:
// 获取或设置一个对象集合,这些对象指定 System.Xml.Serialization.XmlSerializer 如何序列化插入数组(该数组由公共字段或读/写属性返回)的项。
//
// 返回结果:
// System.Xml.Serialization.XmlArrayItemAttributes 对象,它包含 System.Xml.Serialization.XmlArrayItemAttribute
// 对象的集合。
public XmlArrayItemAttributes XmlArrayItems { get; }
//
// 摘要:
// 获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何将公共字段或公共读/写属性 (Property)
// 作为 XML 属性 (Attribute) 序列化。
//
// 返回结果:
// 控制将公共字段或读/写属性 (Property) 序列化为 XML 属性 (Attribute) 的 System.Xml.Serialization.XmlAttributeAttribute。
public XmlAttributeAttribute XmlAttribute { get; set; }
//
// 摘要:
// 获取或设置一个对象,该对象允许区别一组选项。
//
// 返回结果:
// 可应用到某个类成员(被序列化为 xsi:choice 元素)的 System.Xml.Serialization.XmlChoiceIdentifierAttribute。
public XmlChoiceIdentifierAttribute XmlChoiceIdentifier { get; }
//
// 摘要:
// 获取或设置 XML 元素或属性的默认值。
//
// 返回结果:
// 表示 XML 元素或属性的默认值的 System.Object。
public object XmlDefaultValue { get; set; }
//
// 摘要:
// 获取一个对象集合,这些对象指定 System.Xml.Serialization.XmlSerializer 将公共字段或读/写属性序列化为 XML
// 元素的方式。
//
// 返回结果:
// 包含一个 System.Xml.Serialization.XmlElementAttribute 对象集合的 System.Xml.Serialization.XmlElementAttributes。
public XmlElementAttributes XmlElements { get; }
//
// 摘要:
// 获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何序列化枚举成员。
//
// 返回结果:
// 指定 System.Xml.Serialization.XmlSerializer 如何序列化枚举成员的 System.Xml.Serialization.XmlEnumAttribute。
public XmlEnumAttribute XmlEnum { get; set; }
//
// 摘要:
// 获取或设置一个值,该值指定 System.Xml.Serialization.XmlSerializer 是否序列化公共字段或公共读/写属性。
//
// 返回结果:
// 如果 System.Xml.Serialization.XmlSerializer 不应序列化字段或属性,则为 true;否则为 false。
public bool XmlIgnore { get; set; }
//
// 摘要:
// 获取或设置一个值,该值指定当重写包含返回 System.Xml.Serialization.XmlSerializerNamespaces 对象的成员的对象时,是否保留所有的命名空间声明。
//
// 返回结果:
// 如果应保留命名空间声明,则为 true;否则为 false。
public bool Xmlns { get; set; }
//
// 摘要:
// 获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何将类作为 XML 根元素序列化。
//
// 返回结果:
// 重写作为 XML 根元素属性化的类的 System.Xml.Serialization.XmlRootAttribute。
public XmlRootAttribute XmlRoot { get; set; }
//
// 摘要:
// 获取或设置一个对象,该对象指示 System.Xml.Serialization.XmlSerializer 将公共字段或公共读/写属性作为 XML
// 文本序列化。
//
// 返回结果:
// 重写公共属性或字段的默认序列化的 System.Xml.Serialization.XmlTextAttribute。
public XmlTextAttribute XmlText { get; set; }
//
// 摘要:
// 获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何序列化一个已对其应用 System.Xml.Serialization.XmlTypeAttribute
// 的类。
//
// 返回结果:
// 重写应用于类声明的 System.Xml.Serialization.XmlTypeAttribute 的 System.Xml.Serialization.XmlTypeAttribute。
public XmlTypeAttribute XmlType { get; set; }
}
}
xml串行化的一个好处是,它使您能够控制要生成的xml文档的结构。您可以将特殊属性(上表)应用到这个类的成员来完成该功能。下面显示了带xml串行化属性的Category类。
using
System;
using
System.Xml;
using
System.Xml.Serialization;
[XmlRoot(
"
CategoryRoot
"
, Namespace
=
"
http://www.wrox.com
"
, IsNullable
=
false
)]
public
class
Category
{
[XmlAttribute("ID")]
public long CategoryID;
[XmlAttribute("Name")]
public string CategoryName;
[XmlElementAttribute(IsNullable = false)]
public string Description;
}
对Category类做了这些修改后,如果您从浏览器请求程序清单12-2中所示的aspx页时,打开生成的Category.xml应看到下面代码:
<?
xml version="1.0" encoding="utf-8"
?>
<
CategoryRoot
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
ID
="1"
Name
="Beverages"
xmlns
="http://www.wrox.com"
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
</
CategoryRoot
>
正如您所看到的,已经用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
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Category.xml";
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
//Rename the CategoryID to ID and add it as an attribute
XmlAttributeAttribute categoryIDAttribute = new XmlAttributeAttribute();
categoryIDAttribute.AttributeName = "ID";
XmlAttributes attributesIdCol = new XmlAttributes();
attributesIdCol.XmlAttribute = categoryIDAttribute;
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
attrOverrides.Add(typeof(Category), "CategoryID", attributesIdCol);
//Rename the CategoryName to Name and add it as an element
XmlElementAttribute categoryNameElement = new XmlElementAttribute();
categoryNameElement.ElementName = "Name";
XmlAttributes attributesNameCol = new XmlAttributes();
attributesNameCol.XmlElements.Add(categoryNameElement);
attrOverrides.Add(typeof(Category), "CategoryName", attributesNameCol);
XmlSerializer serializer = new XmlSerializer(typeof(Category), attrOverrides);
TextWriter writer = new StreamWriter(xmlFilePath);
// Serialize the Category and close the TextWriter
serializer.Serialize(writer, categoryObj);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Customizing XML Output at runtime
using
XmlAttributeOverrides
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
打开生成的Category.xml应看到下面代码:
<?
xml version="1.0" encoding="utf-8"
?>
<
Category
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
ID
="1"
>
<
Name
>
Beverages
</
Name
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
</
Category
>
注意:
对于每个重写对象,需要一个独特的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类
using
System;
using
System.Xml;
using
System.Xml.Serialization;
[XmlRoot(Namespace
=
"
http://northwind.com/category
"
)]
public
class
Category
{
public long CategoryID;
public string CategoryName;
public string Description;
}
程序清单12-9 使用XmlSerializerNamespaces生成受限的名称
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Category.xml";
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("cate", "http://northwind.com/category");
XmlSerializer serializer = new XmlSerializer(typeof(Category));
TextWriter writer = new StreamWriter(xmlFilePath);
//Serialize the Category and close the TextWriter
serializer.Serialize(writer, categoryObj,namespaces);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Using XmlSerializerNamespaces
class
to generate Qualified Names
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
删除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类为这样的可能性准备了一个构造函数----它期待包含类的类型号,以及描述一些类的类型数组:
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.Collections
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\ArrayList.xml";
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
//Populate the products array
Product prodObj = new Product();
prodObj.ProductID = 1;
prodObj.ProductName = "Chai";
prodObj.QuantityPerUnit = "10 boxes x 20 bags";
prodObj.UnitPrice = "18";
prodObj.UnitsInStock = 39;
ArrayList list = new ArrayList();
list.Add(categoryObj);
list.Add(prodObj);
//Add all the types to the serializer
Type[] extraTypes = new Type[2];
extraTypes[0] = typeof(Category);
extraTypes[1] = typeof(Product);
XmlSerializer serializer = new XmlSerializer(typeof(ArrayList), extraTypes);
TextWriter writer = new StreamWriter(xmlFilePath);
// Serialize the ArrayList and close the TextWriter
serializer.Serialize(writer, list);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Serializing an ArrayList
object
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
然后可以创建一个流,并和以前一样串行化该流。
串行化自定义集合
XmlSerializer也可以处理自定义集合对象,只要这些对象实现这两个.NET Framework 的集合接口之一:IEnumerable 或 ICollection 。.NET Framework提供的所有集合将实现这些接口,因此您可以串行化或反串行化这些集合,而不必增加额外工作。
说明:
只要符合一些条件,像集合(实现ICollection接口的对象)这样的容器对象的内容将被自动串行化;Add方法必须采用简单的正行参数。除了选择适当的对象类型外,当然,您必须确保集合的内容本身符合xml串行化需求,需求如下:每个类必须提供一个默认的构造函数,您应该提供一种通过可用的公开成员或属性访问类中的方式。
程序清单12-10 CategoriesList类的实现
using
System;
using
System.Collections;
using
System.Xml.Serialization;
public
class
CategoriesList
{
private ArrayList _categoriesList;
public CategoriesList()
{
_categoriesList = new ArrayList();
}
public Category[] Categories
{
get
{
Category[] categories = new Category[_categoriesList.Count];
_categoriesList.CopyTo(categories);
return categories;
}
set
{
if (value == null) return;
Category[] categories = (Category[])value;
_categoriesList.Clear();
foreach (Category cate in categories)
_categoriesList.Add(cate);
}
}
public int AddCategory(Category cate)
{
return _categoriesList.Add(cate);
}
}
程序清单12-11 串行化CategoriesList对象
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.Collections
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Categories.xml";
Category category1 = new Category();
category1.CategoryID = 1;
category1.CategoryName = "Beverages";
category1.Description = "Soft drinks, coffees, teas, beers, and ales";
Category category2 = new Category();
category2.CategoryID = 2;
category2.CategoryName = "Condiments";
category2.Description = "Sweet and savory sauces, relishes, spreads, and seasonings";
CategoriesList list = new CategoriesList();
list.AddCategory(category1);
list.AddCategory(category2);
XmlSerializer serializer = new XmlSerializer(typeof(CategoriesList));
TextWriter writer = new StreamWriter(xmlFilePath);
//Serialize the Category and close the TextWriter
serializer.Serialize(writer, list);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Serializing a Collection Object
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
产生的xml结果
<?
xml version="1.0" encoding="utf-8"
?>
<
CategoriesList
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
>
<
Categories
>
<
Category
>
<
CategoryID
>
1
</
CategoryID
>
<
CategoryName
>
Beverages
</
CategoryName
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
</
Category
>
<
Category
>
<
CategoryID
>
2
</
CategoryID
>
<
CategoryName
>
Condiments
</
CategoryName
>
<
Description
>
Sweet and savory sauces, relishes, spreads, and seasonings
</
Description
>
</
Category
>
</
Categories
>
</
CategoriesList
>
1.3 反串行化xml
要反串行化文件Category.xml(在前面的示例中创建的)中的Category对象,您可以直接打开文件流、初始XmlSerializer,并调用Deserialize。
程序清单12-12 将xml文件反串行化为对象
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Category.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Category));
TextReader reader = new StreamReader(xmlFilePath);
//Deserialize the Category and close the TextReader
Category categoryObj = (Category)serializer.Deserialize(reader);
reader.Close();
Response.Write("CategoryID: " + categoryObj.CategoryID + "<br>");
Response.Write("Category Name: " + categoryObj.CategoryName + "<br>");
Response.Write("Category Description: " + categoryObj.Description + "<br>");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Simple XML Deserialization
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
5.处理XmlSerializer引发的事件
如果输入流不符合预期的形式,那么反串行化过程将试图尽最大能力恢复,但是当过程完成时,作为结果的一个或多个对象可以设置为空值,为了帮助处理这些情况,XmlSerializer类发布了4个您可以遇到的事件。
XmlSerializer类的事件
UnknownAttribute 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 属性 (Attribute) 时发生。
UnknownElement 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 元素时发生。
UnknownNode 当 XmlSerializer 在反序列化过程中遇到未知类型的 XML 节点时发生。
UnreferencedObject 在反序列化 SOAP 编码的 XML 流的过程中发生,此时 XmlSerializer 遇到未使用(或未引用)的识别类型。
通过创建适当的委托,可捕获这些事件。
程序清单12-13 处理由XmlSerializer类引发的事件
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
string xmlFilePath = @"C:\Data\Category.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Category));
serializer.UnknownElement += new XmlElementEventHandler(XmlSerializer_UnknownElement);
TextReader reader = new StreamReader(xmlFilePath);
//Deserialize the Category and close the TextReader
Category categoryObj = (Category)serializer.Deserialize(reader);
reader.Close();
Response.Write("<b>Result of Deserialization:" + "</b><br>");
Response.Write("CategoryID: " + categoryObj.CategoryID + "<br>");
Response.Write("Category Name: " + categoryObj.CategoryName + "<br>");
}
void
XmlSerializer_UnknownElement(
object
sender, XmlElementEventArgs e)
{
Category categoryObj = (Category)e.ObjectBeingDeserialized;
Response.Write("<b>Unknown Element:" + "</b><br>");
Response.Write("Unknown Element Name: " + e.Element.Name + "<br>");
Response.Write("Unknown Element Value: " + e.Element.InnerText + "<br><br>");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Handling Events Raised by XmlSerializer
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
该代码假定Category类的声明如下。
public class Category
{
public long CategoryID;
public string CategoryName;
}
正如前面所见,前面示例中用到的Description字段在Category类中消失了。作为程序清单12-13中的xml输入文档使用的xml文件内容如下:
<?
xml version="1.0" encoding="utf-8"
?>
<
Category
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
>
<
CategoryID
>
1
</
CategoryID
>
<
Name
>
Beverages
</
Name
>
<
Description
>
Soft drinks, coffees, teas, beers, and ales
</
Description
>
</
Category
>
输出结果如下:
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 文档
<
Contacts
>
<
ContactID
>
2
</
ContactID
>
<
FirstName
>
Catherine
</
FirstName
>
<
MiddleName
>
R.
</
MiddleName
>
<
LastName
>
Abel
</
LastName
>
<
EmailAddress
>
[email protected]
</
EmailAddress
>
</
Contacts
>
程序清单12-4 Contace类
using
System;
using
System.Xml;
using
System.Xml.Serialization;
[XmlRoot(
"
Contacts
"
)]
public
class
Contact
{
public string ID;
public string FirstName;
public string MiddleName;
public string LastName;
}
程序清单12-5 使用Contace对象将Contact数据映射到AdventureWorks数据库中
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.Collections
"
%>
<%
@ Import Namespace
=
"
System.Web.Configuration
"
%>
<%
@ Import Namespace
=
"
System.Data.SqlClient
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, System.EventArgs e)
{
Contact cont;
//Rename the ContactID to ID element and add it as an attribute
XmlElementAttribute contIDElement = new XmlElementAttribute();
contIDElement.ElementName = "ContactID";
XmlAttributes attributesIdCol = new XmlAttributes();
attributesIdCol.XmlElements.Add(contIDElement);
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
attrOverrides.Add(typeof(Contact), "ID", attributesIdCol);
string connString =
WebConfigurationManager.ConnectionStrings["adventureWorks"].
ConnectionString;
SqlConnection sqlConn = new SqlConnection(connString);
sqlConn.Open();
//Instantiate the SqlCommand object and pass the query to be executed
SqlCommand sqlCommand = new SqlCommand("Select ContactID," +
"FirstName, MiddleName, LastName, EmailAddress from Person.Contact " +
"as Contacts where ContactID = 2 for xml auto, elements", sqlConn);
XmlReader reader = sqlCommand.ExecuteXmlReader();
XmlSerializer serializer = new XmlSerializer(typeof(Contact),
attrOverrides);
serializer.UnknownElement += new
XmlElementEventHandler(XmlSerializer_UnknownElement);
if (serializer.CanDeserialize(reader))
{
cont = (Contact)serializer.Deserialize(reader);
Response.Write("<b>Result of Deserialization:" + "</b><br>");
Response.Write("ID: " + cont.ID + "<br>");
Response.Write("First Name: " + cont.FirstName + "<br>");
Response.Write("Middle Name: " + cont.MiddleName + "<br>");
Response.Write("Last Name: " + cont.LastName + "<br>");
}
else
Response.Write("Cannot serialize data");
}
void
XmlSerializer_UnknownElement(
object
sender, XmlElementEventArgs e)
{
Response.Write("<b>Unknown Element:" + "</b><br>");
Response.Write("Unknown Element Name: " + e.Element.Name + "<br>");
Response.Write("Unknown Element Value: " + e.Element.InnerText +
"<br><br>");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head id
=
"
Head1
"
runat
=
"
server
"
>
<
title
>
Mapping Contacts Data
in
AdventureWorks Database with the Customer Object
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>
输出结果:
Unknown Element:
Unknown Element Name: EmailAddress
Unknown Element Value: [email protected]
Result of Deserialization:
ID: 2
First Name: Catherine
Middle Name: R.
Last Name: Abel
1.4 泛型和XML串行化
CLR使用.NET Framwork2.0 大大增强表达力,泛型类型可增加对运行时的完全支持。xml串行化已延伸到串行化和反串行化的通用类型。
现分析一下泛型类型的代码,见程序清单12-16
using
System;
using
System.Collections;
using
System.Xml.Serialization;
[XmlRoot(
"
NameValuePair
"
)]
public
class
NameValue
<
KeyType, ValueType
>
{
private KeyType _key;
private ValueType _value;
public NameValue()
{
}
public ValueType Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public KeyType Key
{
get
{
return _key;
}
set
{
_key = value;
}
}
}
程序清单12-17 用泛型执行串行化和反串行化
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
private
string
_xmlFilePath
=
@"
C:\Data\NameValue.xml
"
;
void
Serialize(
object
sender, EventArgs e)
{
NameValue<int, string> nameVal = new NameValue<int, string>();
nameVal.Key = 1;
nameVal.Value = "Manufacturing";
XmlSerializer serializer = new XmlSerializer(typeof(NameValue<int, string>));
TextWriter writer = new StreamWriter(_xmlFilePath);
//Serialize the NameValue object and close the TextWriter
serializer.Serialize(writer, nameVal);
writer.Close();
lblResult.Text = "File written successfully";
}
void
Deserialize(
object
sender, EventArgs e)
{
XmlSerializer serializer = new XmlSerializer(typeof(NameValue<int, string>));
TextReader reader = new StreamReader(_xmlFilePath);
//Deserialize the Category and close the TextReader
NameValue<int, string> nameVal = (NameValue<int, string>)serializer.Deserialize(reader);
reader.Close();
lblResult.Text = "Key : " + nameVal.Key + "<br>";
lblResult.Text += "Value: " + nameVal.Value;
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Using Generics
for
Serialization and Deserialization
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
<
asp:Button runat
=
"
Server
"
ID
=
"
btnSerialize
"
OnClick
=
"
Serialize
"
Text
=
"
Serialize
"
/>
<
asp:Button runat
=
"
Server
"
ID
=
"
btnDeserialize
"
OnClick
=
"
Deserialize
"
Text
=
"
Deserialize
"
/>
<
br
/>
<
br
/>
<
asp:Label runat
=
"
Server
"
ID
=
"
lblResult
"
Height
=
"
21px
"
Width
=
"
351px
"
/>
</
div
>
</
form
>
</
body
>
</
html
>
串行化泛型集合
除了创建简单的泛型类型外,您还可以创建强类型化泛型集合,这些集合的类型安全和性能比非泛型的强类型化集合更好。System.Collection.Generic命名空间包含若干用于定义泛型集合的接口和类。看一下下列代码,理解如何用List类创建强类型化的类别集合对象。
List<Category> list = new List<Category>();
程序清单12-18 Serializing Typed Generics Collections
<%
@ Page Language
=
"
C#
"
%>
<%
@ Import Namespace
=
"
System.IO
"
%>
<%
@ Import Namespace
=
"
System.Collections.Generic
"
%>
<%
@ Import Namespace
=
"
System.Xml.Serialization
"
%>
<
script runat
=
"
server
"
>
void
Page_Load(
object
sender, EventArgs e)
{
string xmlFilePath = @"C:\Data\GenericCollections.xml";
List<Category> list = new List<Category>();
Category categoryObj = new Category();
categoryObj.CategoryID = 1;
categoryObj.CategoryName = "Beverages";
categoryObj.Description = "Soft drinks, coffees, teas, beers, and ales";
list.Add(categoryObj);
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>));
TextWriter writer = new StreamWriter(xmlFilePath);
//Serialize the Collection object and close the TextWriter
serializer.Serialize(writer, list);
writer.Close();
Response.Write("File written successfully");
}
</
script
>
<
html xmlns
=
"
http://www.w3.org/1999/xhtml
"
>
<
head runat
=
"
server
"
>
<
title
>
Serializing Typed Generics Collections
</
title
>
</
head
>
<
body
>
<
form id
=
"
form1
"
runat
=
"
server
"
>
<
div
>
</
div
>
</
form
>
</
body
>
</
html
>