摘要:XML序列化與反序列化
這次有機會處理一組頗大型的xml檔,資料很簡單確很大。在同事的指導下,學習了用序列化去處理這類的問題,並籍此學習序列化。
檔案的格式如下:
Candys.xml
<?xmlversion="1.0"?>
<Box>
<Candy>
<Taste>Chocolate</Taste>
</Candy>
<Candy>
<Taste>Strawberry</Taste>
</Candy>
<Candy>
<Taste>Milk</Taste>
</Candy>
</Box>
當然資料量遠大於此範例;但是這個範例已經足夠讓我們作為序列化的參考範本了。
依上述的xml檔,我們寫了兩個類別如下:
Box 類別
[Serializable]
public class Box
{
private Candy[] candys = null;
public Candy[] Candys
{
get { return candys; }
set { candys = value; }
}
}
Candy類別
[Serializable]
public class Candy
{
private String taste = string.Empty;
public String Taste
{
get { return taste; }
set { taste = value; }
}
}
接下來我們看主程式:
{
Stopwatch sw = new Stopwatch();
FileStream f = new FileStream("./Candys.xml", FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(f);
sw.Start();
string XmlStr = sr.ReadToEnd();
//同事提供的反序列化範例
XmlSerializer xs = new XmlSerializer(typeof(Box));
MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(XmlStr));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
Box box = xs.Deserialize(memoryStream) as Box;
//
Console.WriteLine(box.Candys.Length);
sw.Stop();
Console.WriteLine("Test : {0}ms",
sw.ElapsedMilliseconds);
}
{
Console.Read();
}
很可惜,這樣的程式是不能夠執行的,必須對我們的xml檔作調整才有辦法執行。修正xml檔如下:
<?xmlversion="1.0"?>
<Box>
<Candys>
<Candy>
<Taste>Chocolate</Taste>
</Candy>
<Candy>
<Taste>Strawberry</Taste>
</Candy>
<Candy>
<Taste>Milk</Taste>
</Candy>
</Candys>
</Box>
差別的地方在<Box> tag裡面再加入一組<Candys> tag雖然這個折中的方法可以解決當時的問題,但是實際上我的xml檔有近千個,每個檔案大小約2x M。所以後來還是研究了一下XML序列化與反序列化的規則。要怎麼設計出合適的類別來處理你的xml檔,而不是去修改xml檔(當然前提你的xml檔要真得符合xml的規則)。
這裡先賣個關子,我們來看看要如何修改類別,讓本來的xml檔就可以順利的反序化成物件。
Box類別
[Serializable]
public class Box
{
private Candy[] candys = null;
///
[System.Xml.Serialization.XmlElementAttribute("Candy", Form =System.Xml.Schema.XmlSchemaForm.Unqualified)]
public Candy[] Candys
{
get { return candys; }
set { candys = value; }
}
}
Candy類別
[Serializable]
public class Candy
{
private String taste = string.Empty;
public String Taste
{
get { return taste; }
set { taste = value; }
}
}
由結果可以得知,加入
[System.Xml.Serialization.XmlElementAttribute("Candy", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
這行後,就不再需要為xml檔額外的加入<Candys>這組tag了。
不過小弟並不是很了解它其中的意義與邏輯,還希望有高手能夠指點一番。
但是其實xml檔案可以很簡單,也可以很複雜,要辛辛苦苦手工打造出可以序列化的類別並不是那麼容易。參考這篇文章,http://stackoverflow.com/questions/364253/how-to-deserialize-xml-document
微軟提供非常好用的工具xsd.exe,作法也非常簡單,只要擁有相對應的xml檔與三個步驟,讓人可以輕鬆產出可序列化的類別。
1. 以vs2010為例,執行Visual Studio 的命令提示字元,它會在 Visual Studio Tools底下。開啟後,輸入以下指令,xsd.exe C:\Candys.xml ,會產生Candys.xsd檔。
2. 取得Candys.xsd後,我們執行xsd.exe C:\Candys.xsd /Classes,這個指令會產生我們所需要的類別檔。
3. 在專案中載入此類別檔,完成。(當然我已經將之前寫的Box和Candy類別自專案中移除)。
產生的類別檔如下,而且保證可以序列化/反序列化。
//------------------------------------------------------------------------------
//
// 這段程式碼是由工具產生的。
// 執行階段版本:4.0.30319.239
//
// 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼,
// 變更將會遺失。
//
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// 此原始程式碼由 xsd 版本=4.0.30319.1 自動產生。
//
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class Box {
private BoxCandy[] itemsField;
///
[System.Xml.Serialization.XmlElementAttribute("Candy", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public BoxCandy[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class BoxCandy {
private string tasteField;
///
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string Taste {
get {
return this.tasteField;
}
set {
this.tasteField = value;
}
}
}