XmlSerializer 序列化與反序列化CDATA 以及實現
XmlSerializer類可以幫助我們將物件的狀態序列化對儲存,同時通過反序列化,可以還原物件的狀態。通過與XML序列化與反序列化相關的特性(Attribute),可以控制我們的序列化的XML格式;同時通過實現IXmlSerializable介面,可以自定義的實現序列化與反序列化。關於此類,詳情檢視MSDN XmlSerializer類。不過在一些特殊情況下,CDATA型別的序列化與反序列化並不那麼容易,或者說會出現一定的問題,本文將對此進行探討。
1,新建測試專案和窗體
窗體程式碼如下:
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
namespace XmlSerializerTest
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private void buttonSerialize_Click(object sender, EventArgs e)
{
var tc = new TestClass();
tc.ID = 2011;
tc.CDataContent = @"<table id=""test"">test";
var serializer = new XmlSerializer(tc.GetType());
var text = new StringBuilder();
using (var s = new StringWriter(text))
{
serializer.Serialize(s, tc);
}
this.textBoxXml.Text = text.ToString();
}
private void buttonDeserialize_Click(object sender, EventArgs e)
{
var serializer = new XmlSerializer(typeof(TestClass));
using (var s = new StringReader(this.textBoxXml.Text))
{
var tc = serializer.Deserialize(s) as TestClass;
if (tc != null)
{
MessageBox.Show(tc.CDataContent);
}
}
}
/// <summary>
/// Test class
/// </summary>
[Serializable]
public class TestClass
{
[XmlElement("ID")]
public int ID { get; set; }
[XmlElement("Content")]
public string CDataContent { get; set; }
}
}
}
執行,點選Serialize按鈕,可以得到序列化生成的XML文件:
<?xml version="1.0" encoding="utf-16"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>2011</ID>
<Content><table id="test">test</Content>
</TestClass>
點選Deserialize按鈕,可以得到以下結果:
---------------------------
---------------------------
<table id="test">test
---------------------------
OK
---------------------------
2,序列化為CDATA
如果需要將序列化生成的XML轉化為以下格式(在實際中往往我們需要的是這種效果,而不是被轉義的字元):
<?xml version="1.0" encoding="utf-16"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>2011</ID>
<Content>
<![CDATA[
<table id="test">test]]>
</Content>
</TestClass>
這個時候我們至少有兩種方法來實現。
2.1,修改實體類
我們將實體類TestClass修改為以下(關於為什麼需要這樣定義,請大家自行思考):
/// <summary>
/// Test class
/// </summary>
[Serializable]
public class TestClass
{
[XmlElement("ID")]
public int ID { get; set; }
[XmlIgnore]
public string CDataContent { get; set; }
[XmlElement("Content")]
public XmlNode[] Nodes
{
get
{
var dom = new XmlDocument();
return new XmlNode[] { dom.CreateCDataSection(this.CDataContent) };
}
set
{
if (value == null)
{
this.CDataContent = null;
return;
}
if (value.Length != 1)
throw new InvalidOperationException("Invalid array.");
var content = value[0];
if (null == content)
throw new InvalidOperationException("Node is null.");
this.CDataContent = content.Value;
}
}
}
執行後,TextBox的內容為:
<?xml version="1.0" encoding="utf-16"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>2011</ID>
<Content><![CDATA[<table id="test">test]]></Content>
</TestClass>
已經實現。
2.2,實現IXmlSerializable介面
/// <summary>
/// Test class
/// </summary>
[Serializable]
public class TestClass : IXmlSerializable
{
[XmlElement("ID")]
public int ID { get; set; }
[XmlElement("Content")]
public string CDataContent { get; set; }
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.ReadStartElement("TestClass");
this.ID=reader.ReadElementContentAsInt("ID", "");
this.CDataContent=reader.ReadElementContentAsString("Content", "");
reader.ReadEndElement();
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteStartElement("ID");
writer.WriteValue(this.ID);
writer.WriteEndElement();
writer.WriteStartElement("Content");
writer.WriteCData(this.CDataContent);
writer.WriteEndElement();
}
}
請留意ReadXml與WriteXml兩個方法。一個將Xml的值讀取出來賦值給當前例項,一個將當前例項的屬性值寫入到XML;而另外一個GetSchema在返回此物件對應的XML架構用於驗證是否為合法的XML文件。
經過測試,上述方法可以達到第一種方法的效果,而且更加的安全,只不過在實現介面的時候需要麻煩一點。
對於一些無法直接序列化的類比如泛型的List等,也可以按照這種方式實現自定義的序列化與反序列化。