关注XML串行化
XmlSerializer 則提供了另外的方法, 它使你能夠將自己的物件串列和反串行化爲XML. 串列化資料既能夠讓你像處理文件一樣對資料進行隨機存取, 同時又能夠跳過你不感興趣的元素. 在本文中, 我將向你展示如何使用XmlSerializer類以及如何在你的類中添加屬性來控制串列化過程.
XmlSerializer
XmlSerializer類存在於System.Xml.Serialization命名空間的System.Xml.dll中, 它用一種高度鬆散耦合的方式提供串列化服務. 你的類不需要繼承特別的基類, 而且它們也不需要實現任何特別的介面. 相反的, 你只需要在你的類或者這些類的公共域以及讀/寫屬性里加上自定義的屬性. XmlSerializer 通過相反映射讀取這些屬性並用它們將你的類和類成員映射到XML元素和屬性.
1.將XML 映射到物件
a. Theater.xml
<?xml version="1.0" encoding="BIG5" ?>
<theater>
<name>华纳威</name>
<phone>(888)665-2222</phone>
<movie minutes="120" stars="2">
<title>下水道4-终极下水道</title>
<rating>R</rating>
<showing>16:15:00</showing>
<showing>19:05:00</showing>
<showing>21:40:00</showing>
</movie>
<movie minutes="100">
<title>大话指环王</title>
<rating>PG-13</rating>
<showing>16:00:00</showing>
<showing>19:00:00</showing>
<showing>21:40:00</showing>
</movie>
</theater>
接下来定義了一個Theater(電影院)類, 它包含了XmlSerializer使用的屬性映射.
using System;
using System.Xml.Serialization;
namespace Theater
{
[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元素.
一個帶有屬性映射的Movie類
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文件.
using System;
using System.Xml.Serialization;
using System.IO;
namespace Theater
{
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 );
}
}
}
}
主要的程式碼都放在Main 函數的try代碼段?. 首先創建一個XmlSerializer物件並指明一個System.Type 物件來告訴反串行化程式要創建的物件的類型. typeof操作符爲Theater類返回一個System.Type 物件. 然後, 打開一個文件流讀取輸入的XML文件. 調用XmlSerializer的Deserialize方法, 並把文件流傳遞給它. Deserialize 返回對Theater物件的引用. Theater和Movie 物件中的ToString方法能夠讓你簡單的輸出它們.
2. 將物件串列化到XML?
從一個Theater物件生成XML資料同樣是容易的. 表E中的程式,XmlOut, 就是將一個Theater物件串列化到XML 文件?. 這個程式通過命令行執行, 你需要指明輸出的XML文件.
using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace Theater
{
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 );
}
}
}
}
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時間再減去6個小時.
移動資料是小菜一碟
XmlSerializer 使得在物件和XML間移動資料變得非常容易, 只要在類里加上XML映射屬性. 但是對於更複雜的物件模型, 手工的創建XML映射會變得非常的麻煩而且容易出錯. 在我的下一篇文章?, 我將告訴你如何自動化這個工作並實現對你的XML資料的更嚴格的控制.