XML序列化私有欄位
假設今天有個Person的類別如下。除了名字、年齡、性別外,內含_friends清單用以提供IsFriend與AddFriend函式所須用到的朋友清單資料。
VB.NET
Public Class Person
#Region "Enum"
Enum SexType
Boy
Girl
End Enum
#End Region
#Region "Var"
Private _name As String
Private _year As Integer
Private _sex As SexType
Private _friendNames As New List(Of String)
#End Region
#Region "Property"
Public Property Name() As String
Get
If String.IsNullOrEmpty(_name) Then
Return String.Empty
End If
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Property Year() As Integer
Get
Return _year
End Get
Set(ByVal value As Integer)
_year = value
End Set
End Property
Public Property Sex() As SexType
Get
Return _sex
End Get
Set(ByVal value As SexType)
_sex = value
End Set
End Property
#End Region
#Region "Public Method"
Public Sub AddFriend(ByVal people As Person)
_friendNames.Add(people.Name)
End Sub
Public Function IsFriend(ByVal people As Person) As Boolean
For Each friendName As String In _friendNames
If friendName = people.Name Then
Return True
End If
Next
Return False
End Function
#End Region
End Class
C#
public class Person
{
#region Enum
public enum SexType
{
Boy,
Girl
}
#endregion
#region Var
private string _name;
private int _year;
private SexType _sex;
private List<String> _friendNames = new List<String>();
#endregion
#region Property
public string Name
{
get
{
if (string.IsNullOrEmpty(_name))
{
return string.Empty;
}
return _name;
}
set { _name = value; }
}
public int Year
{
get { return _year; }
set { _year = value; }
}
public SexType Sex
{
get { return _sex; }
set { _sex = value; }
}
#endregion
#region Public Method
public void AddFriend(Person people)
{
_friendNames.Add(people.Name);
}
public bool IsFriend(Person people)
{
foreach (String friendName in _friendNames)
{
if (friendName == people.Name)
{
return true;
}
}
return false;
}
#endregion
}
在.NET程式裡,若將該類別直接做XML序列化,則其類別的_friends這個私有的欄位資料將不會被序列化保存 (因為.NET XML序列化無法序列化私有欄位資料)。此時我們可以用自定義XML的方式來去解決此問題。透過自定義XML序列化,我們可以自己定義XML序列化的格式。也可以把本來不會序列化的私有欄位一併序列化。
但是,若只是很單純的去實作自定義XML序列化。做出來的類別將無法與.NET本身的XML序列化做整合。舉個例子來說,假設今天Person這個類別實作了自定義XML序列化。而程式中又有個PersonCollection這個類別內含Person這個類別成員。則此時,若PersonCollection這個類別沒有實作自定義XML序列化,而想直接用來序列化的話。該程式在序列化時將會發生錯誤。也就是說,當A這個類別做了自定義XML序列化,而B這個類別的成員內含A這個類別時,則B通常也將需要自定義XML序列化。
要解決該問題,照著下面程式碼片段的格式去實作自定義XML序列化即可。
VB.NET
#Region "Implement IXmlSerializable"
Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
Return Nothing
End Function
Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
Dim startElementName As String = reader.Name
Dim currentElementName As String
Me._friendNames.Clear()
Do
currentElementName = reader.Name
If currentElementName = startElementName AndAlso (reader.MoveToContent = Xml.XmlNodeType.EndElement OrElse reader.IsEmptyElement) Then
reader.Read()
Exit Do
End If
Select Case currentElementName
Case "Name"
Me.Name = reader.ReadElementString()
Case "Sex"
Me.Sex = CType(reader.ReadElementString(), SexType)
Case "Year"
Me.Year = CInt(reader.ReadElementString())
Case "Friend"
Me._friendNames.Add(reader.GetAttribute("Name"))
reader.Read()
Case Else
reader.Read()
End Select
Loop
End Sub
Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
writer.WriteElementString("Name", Name)
writer.WriteElementString("Sex", CInt(Sex).ToString)
writer.WriteElementString("Year", Year.ToString)
For Each friendName As String In _friendNames
writer.WriteStartElement("Friend")
writer.WriteAttributeString("Name", friendName)
writer.WriteEndElement()
Next
End Sub
#End Region
C#
#region Implement IXmlSerializable
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(System.Xml.XmlReader reader)
{
string startElementName = reader.Name;
string currentElementName;
this._friendNames.Clear();
do
{
currentElementName = reader.Name;
if (currentElementName == startElementName && (reader.MoveToContent() == System.Xml.XmlNodeType.EndElement || reader.IsEmptyElement))
{
reader.Read();
break;
}
switch (currentElementName)
{
case "Name":
this.Name = reader.ReadElementString();
break;
case "Sex":
this.Sex = (SexType)int.Parse (reader.ReadElementString());
break;
case "Year":
this.Year = int.Parse(reader.ReadElementString());
break;
case "Friend":
this._friendNames.Add(reader.GetAttribute("Name"));
reader.Read();
break;
default:
reader.Read();
break;
}
}while (true);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString("Name", Name);
writer.WriteElementString("Sex", ((int)Sex).ToString());
writer.WriteElementString("Year", Year.ToString());
foreach (string friendName in _friendNames)
{
writer.WriteStartElement("Friend");
writer.WriteAttributeString("Name", friendName);
writer.WriteEndElement();
}
}
#endregion
其主要重點概念如下圖所示: