人们一直高喊xml是解决系统互联问题的关键, 而.net framework 也为处理xml数据提供了许多不同的类库. xmldocument 类能让你像处理文件一样处理xml 数据, 而xmlreader, xmlwriter, 和它们的派生类使你能够将xml 数据做为数据流处理. xmlserializer 则提供了另外的方法, 它使你能够将自己的对象串行和反串行化为xml. 串行化数据既能够让你像处理文件一样对数据进行随机存取, 同时又能够跳过你不感兴趣的元素. 在本文中, 我将向你展示如何使用xmlserializer类以及如何在你的类中添加属性来控制串行化过程.
xmlserializer
xmlserializer类存在于system.xml.serialization命名空间的system.xml.dll中, 它用一种高度松散耦合的方式提供串行化服务. 你的类不需要继承特别的基类, 而且它们也不需要实现任何特别的接口. 相反的, 你只需要在你的类或者这些类的公共域以及读/写属性里加上自定义的属性. xmlserializer 通过相反映射读取这些属性并用它们将你的类和类成员映射到xml元素和属性.
将xml 映射到对象
考虑表a中的xml语句, 哪一个正确的描述了一家电影院中上映的电影呢?
表a
<?xml version="1.0" encoding="utf-8" ?>
<theater>
<name>the camelot</name>
<phone>(888)665-2222</phone>
<movie minutes="120" stars="2">
<title>the score</title>
<rating>r</rating>
<showing>16:15:00</showing>
<showing>19:05:00</showing>
<showing>21:40:00</showing>
</movie>
<movie minutes="100">
<title>shrek</title>
<rating>pg-13</rating>
<showing>16:00:00</showing>
<showing>19:00:00</showing>
<showing>21:40:00</showing>
</movie>
</theater>
表b中定义了一个theater(电影院)类, 它包含了xmlserializer使用的属性映射.
表b
using system;
using system.xml.serialization;
namespace articles.techrepublic.xmlserialization
{
[xmlroot( "theater" )]
public class theater
{
[xmlelement( "name" )]
public string name = "";
[xmlelement( "phone" )]
public string phone = "";
[xmlelement( "movie" )]
public movie[] movies;
public override string tostring()
{
string movies = "";
if ( movies != null )
foreach ( movie movie in movies )
movies += "\n" + movie.tostring();
return string.format( "{0}\n {1}\n{2}",
name, phone, movies );
}
}
xmlroot 属性将类theater映射到xml的根元素theater. xmlelement 属性将name, phone, 和 movies数据域映射到嵌套在theater元素中的name, phone, 和 movie xml元素上去. 因为movies是movie数组, 所以xmlserializer将它映射到多个xml movie元素.
表c展示了一个带有属性映射的movie类
表c
public class movie
{
[xmlelement( "title" )]
public string title = "";
[xmlattribute( "minutes" )]
public uint minutes = 0;
[xmlelement( "showing", datatype="time" )]
public datetime[] showings;
public override string tostring()
{
string showings = "";
if ( showings != null )
{
showings = "shows at ";
foreach ( datetime showing in showings )
showings += showing.toshorttimestring() + " ";
}
else
{
showings = "- no showings";
}
return string.format( " {0} ({1} min) {2}",
title, minutes, showings );
}
}
xmlelement 属性将title和showings数据域映射到movie元素内的title 和showing xml元素.就象 theater.movie一样, 做为datetime数组的movie.showings 被映射到多个xml showing 元素. showing 数据域的属性包括位置属性参数datatype="time". 它将datetime值映射到一个xml time值, 其间去掉了日期信息而只保留了时间信息. xmlattribute 属性将minutes 数据域映射到xml属性而不是xml元素.
xml数据中的moviestars(影星)属性和rating(上座率)元素没有被映射到movie类中的任何东西上去. 当反串行化xml数据的时候, xmlserializer只是简单的跳过它不能映射的项目. 当串行化一个对象的时候, 你可以在公共数据域和你希望xmlserializer跳过的属性里加上xmlignore 属性.
xmlroot, xmlelement, 和 xmlattribute的属性类都应包括后缀"attribute." 在我的属性申明里, 我使用了没有后缀的缩写形式. theater和movie类中的公共属性可以被改写成公共属性以求得更好的封装性. xmlserializer 可以用相同的方式使用它们. 我在这里将它们做为数据域使用是为了使代码更紧凑.
将xml数据反串行化成对象
将xml数据加载到一个theater对象里现在已经变得非常容易. 表d中的程序, xmlin, 通过反串行化movie showings xml 数据创建一个theater对象. 这个程序通过命令行执行, 你需要指明一个输入的xml文件.
表d
using system;
using system.xml.serialization;
using system.io;
using articles.techrepublic.xmlserialization;
public class xmlin
{
public static void main( string[] args )
{
if ( args.length != 1 )
{
console.writeline( "usage: xmlin infile.xml" );
return;
}
try
{
// deserialize the specified file to a theater object.
xmlserializer xs = new xmlserializer( typeof ( theater ) );
filestream fs = new filestream( args[0], filemode.open );
theater theater = (theater)xs.deserialize( fs );
// display the theater object.
console.writeline ( theater );
}
catch ( exception x )
{
console.writeline( "exception: " + x.message );
}
}
}
output:
>xmlin theaterin.xml
the camelot
(888)665-2222
the score (120 min) shows at 4:15 pm 7:05 pm 9:40 pm
shrek (100 min) shows at 4:00 pm 7:00 pm 9:40 pm
主要的程序代码都放在main 函数的try代码段里. 首先创建一个xmlserializer对象并指明一个system.type 对象来告诉反串行化程序要创建的对象的类型. typeof操作符为theater类返回一个system.type 对象. 然后, 打开一个文件流读取输入的xml文件. 调用xmlserializer的deserialize方法, 并把文件流传递给它. deserialize 返回对theater对象的引用. theater和movie 对象中的tostring方法能够让你简单的输出它们.
将对象串行化到xml里
从一个theater对象生成xml数据同样是容易的. 表e中的程序,xmlout, 就是将一个theater对象串行化到xml 文件里. 这个程序通过命令行执行, 你需要指明输出的xml文件.
表e
using system;
using system.xml;
using system.xml.serialization;
using system.io;
using articles.techrepublic.xmlserialization;
public class xmlout
{
// returns a populated theater object.
public static theater gettheater()
{
movie movie = new movie();
movie.title = "o brother, where art thou?";
movie.minutes = 102;
movie.showings = new datetime[3];
movie.showings[0] = new datetime( 2001, 8, 2, 13, 15, 0 );
movie.showings[1] = new datetime( 2001, 8, 2, 16, 30, 0 );
movie.showings[2] = new datetime( 2001, 8, 2, 19, 55, 0 );
theater theater = new theater();
theater.name = "hollywood movies 10";
theater.phone = "(972)555-154";
theater.movies = new movie[1];
theater.movies[0] = movie;
return theater;
}
public static void main( string[] args )
{
if ( args.length != 1 )
{
console.writeline( "usage: xmlout outfile.xml" );
return;
}
try
{
theater theater = gettheater();
// serialize the theater object to an xml file.
xmlserializer xs = new xmlserializer( typeof ( theater ) );
filestream fs = new filestream( args[0], filemode.create );
xs.serialize( fs, theater );
}
catch ( exception x )
{
console.writeline( "exception: " + x.message );
}
}
}
invocation:
>xmlout theaterout.xml
theaterout.xml contents:
<?xml version="1.0"?>
<theater
xmlns:xsi=http://www.w3.org/2001/xmlschema-instance
xmlns:xsd="http://www.w3.org/2001/xmlschema">
<name>hollywood movies 10</name>
<phone>(972)555-154</phone>
<movie minutes="102">
<title>o brother, where art thou?</title>
<showing>13:15:00.0000000-06:00</showing>
<showing>16:30:00.0000000-06:00</showing>
<showing>19:55:00.0000000-06:00</showing>
</movie>
</theater>
主要的程序代码都放在main 函数的try代码段里. 首先通过gettheater帮助函数创建一个theater对象. 然后, 打开一个文件流来生成输出的xml 文件. 调用xmlserializer的serialize方法, 传递给它文件流和theater对象. 就是这样简单–xml文件生成了!
输出的theater 元素包含了为模板和模板实例命名空间生成的xml命名空间属性(xmlns), 虽然在这两个命名空间里这些数据并不代表任何东西. showing元素中的-06:00 指的是美国中部时间, 或者说gmt时间再减去个小时, 也就是我所在的时区.
移动数据是小菜一碟
xmlserializer 使得在对象和xml间移动数据变得非常容易, 只要在类里加上xml映射属性. 但是对于更复杂的对象模型, 手工的创建xml映射会变得非常的麻烦而且容易出错. 在我的下一篇文章里, 我将告诉你如何自动化这个工作并实现对你的xml数据的更严格的控制.
