xml 之 十二 串行化

        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 getset; }
        
//
        
// 摘要:
        
//     获取要重写的 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 getset; }
        
//
        
// 摘要:
        
//     获取或设置一个对象集合,这些对象指定 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 getset; }
        
//
        
// 摘要:
        
//     获取或设置一个对象,该对象允许区别一组选项。
        
//
        
// 返回结果:
        
//     可应用到某个类成员(被序列化为 xsi:choice 元素)的 System.Xml.Serialization.XmlChoiceIdentifierAttribute。
        public XmlChoiceIdentifierAttribute XmlChoiceIdentifier get; }
        
//
        
// 摘要:
        
//     获取或设置 XML 元素或属性的默认值。
        
//
        
// 返回结果:
        
//     表示 XML 元素或属性的默认值的 System.Object。
        public object XmlDefaultValue getset; }
        
//
        
// 摘要:
        
//     获取一个对象集合,这些对象指定 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 getset; }
        
//
        
// 摘要:
        
//     获取或设置一个值,该值指定 System.Xml.Serialization.XmlSerializer 是否序列化公共字段或公共读/写属性。
        
//
        
// 返回结果:
        
//     如果 System.Xml.Serialization.XmlSerializer 不应序列化字段或属性,则为 true;否则为 false。
        public bool XmlIgnore getset; }
        
//
        
// 摘要:
        
//     获取或设置一个值,该值指定当重写包含返回 System.Xml.Serialization.XmlSerializerNamespaces 对象的成员的对象时,是否保留所有的命名空间声明。
        
//
        
// 返回结果:
        
//     如果应保留命名空间声明,则为 true;否则为 false。
        public bool Xmlns getset; }
        
//
        
// 摘要:
        
//     获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何将类作为 XML 根元素序列化。
        
//
        
// 返回结果:
        
//     重写作为 XML 根元素属性化的类的 System.Xml.Serialization.XmlRootAttribute。
        public XmlRootAttribute XmlRoot getset; }
        
//
        
// 摘要:
        
//     获取或设置一个对象,该对象指示 System.Xml.Serialization.XmlSerializer 将公共字段或公共读/写属性作为 XML
        
//     文本序列化。
        
//
        
// 返回结果:
        
//     重写公共属性或字段的默认序列化的 System.Xml.Serialization.XmlTextAttribute。
        public XmlTextAttribute XmlText getset; }
        
//
        
// 摘要:
        
//     获取或设置一个对象,该对象指定 System.Xml.Serialization.XmlSerializer 如何序列化一个已对其应用 System.Xml.Serialization.XmlTypeAttribute
        
//     的类。
        
//
        
// 返回结果:
        
//     重写应用于类声明的 System.Xml.Serialization.XmlTypeAttribute 的 System.Xml.Serialization.XmlTypeAttribute。
        public XmlTypeAttribute XmlType getset; }
    }

}


 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 == nullreturn;
            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>catherine0@adventure-works.com</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: 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

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
<intstring> nameVal = new NameValue<intstring>();
        nameVal.Key 
= 1;
        nameVal.Value 
= "Manufacturing";
        XmlSerializer serializer 
= new XmlSerializer(typeof(NameValue<intstring>));
        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<intstring>));
        TextReader reader 
= new StreamReader(_xmlFilePath);
        
//Deserialize the Category and close the TextReader
        NameValue<intstring> nameVal = (NameValue<intstring>)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>

posted on 2007-09-28 16:41  执法长老  阅读(840)  评论(1编辑  收藏  举报

导航