學藝不精,陸續踩了幾次雷,整理DataContract與DataMember對序列化的影響備忘。
WCF預設使用DataContractSerializer執行序列化,而DataContractSerializer可依物件類別標註的[DataContract]及[DataMember]、[IgnoreDataMember]等Attribute決定哪些屬性該序列化。依照MSDN文件,DataContractSerializer的處理原則為:
- 類別標示[DataContract]時,只序列化有標示[DataMember]的項目
註:DataMember不限於public Property,可用於各種存取層級(public、private、internal、protected)的Property或Field。 - 若類別未標示[DataContract],DataContractSerializer預設會序列化「所有可讀寫的public Property及Field」,此時可透過[IgnoreDataMember]負向表列不要序列化的Property或Field。
我設計了以下幾個型別來觀察DataContractSerializer行為,有Property有Field,有public有private,再配合[DataContract]、[DataMember]、[IgnoreDataMember]組合出不同變化:
using System.Runtime.Serialization;
[DataContract]
publicclass Blah1
{
publicint PropNoAttr { get; set; }
publicint Field;
privateint PrivProp { get; set; }
privateint PrivField;
}
[DataContract]
publicclass Blah2
{
publicint PropNoAttr { get; set; }
[DataMember]
publicint PropAttr { get; set; }
publicint Field;
[DataMember]
privateint PrivProp { get; set; }
[DataMember]
privateint PrivField;
}
[DataContract]
publicclass Blah3
{
publicint PropNoAttr { get; set; }
[IgnoreDataMember]
publicint PropIgnoreAttr { get; set; }
publicint Field;
privateint PrivProp { get; set; }
privateint PrivField;
}
publicclass Blah4
{
publicint PropNoAttr { get; set; }
[DataMember]
publicint PropAttr { get; set; }
[IgnoreDataMember]
publicint PropIgnoreAttr { get; set; }
publicint Field;
privateint PrivProp { get; set; }
privateint PrivField;
}
publicclass Blah5
{
publicint PropNoAttr { get; set; }
publicint ReadonlyProp { get; }
publicint Field;
privateint PrivProp { get; set; }
privateint PrivField;
}
測試程式很簡單,用DataContractSerializer將Blah1到Blah5序列化成XML,Console.WriteLine出來,借用XDocument將XML以縮排格式輸出。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
staticvoid Main(string[] args)
{
Console.WriteLine(Serialize<Blah1>(new Blah1()));
Console.WriteLine(Serialize<Blah2>(new Blah2()));
Console.WriteLine(Serialize<Blah3>(new Blah3()));
Console.WriteLine(Serialize<Blah4>(new Blah4()));
Console.WriteLine(Serialize<Blah5>(new Blah5()));
Console.Read();
}
staticstring Serialize<T>(T graph)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, graph);
XDocument xd = XDocument.Parse(Encoding.UTF8.GetString(ms.ToArray()));
return
typeof(T).ToString() +
"\n=====================================\n" +
xd.ToString() + "\n";
}
}
}
執行結果如下:
Blah1
=====================================
<Blah1xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>
Blah2
=====================================
<Blah2xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<PrivField>0</PrivField>
<PrivProp>0</PrivProp>
<PropAttr>0</PropAttr>
</Blah2>
Blah3
=====================================
<Blah3xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>
Blah4
=====================================
<Blah4xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Field>0</Field>
<PropAttr>0</PropAttr>
<PropNoAttr>0</PropNoAttr>
</Blah4>
Blah5
=====================================
<Blah5xmlns="http://schemas.datacontract.org/2004/07/"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Field>0</Field>
<PropNoAttr>0</PropNoAttr>
</Blah5>
- Blah1
只標了[DataContract]沒標任何[DataMember],XML空空如也,沒有任何Property或Field被序列化 - Blah2
標註[DataContract]後,所有標註[DataMember]的成員都包含在XML中,不論public或private,不管是Property或是Field - Blah3
標註[DataContract]後DataContractSerializer只認[DataMember]辦事,[IgnoreDataMember]沒有作用,XML是空的 - Blah4
未標註[DataContract],除了[IgnoreDataMember] PropIgnoreAttr外,所有public的Property及Field都被序列化 - Blah4
未加任何Attribute,DataContractSerializer將序列化所有公開Property及Field,但Property限定可讀寫者,刻意放了一個public ReadonlyProp { get; }唯讀屬性,果然被排除了。
實驗驗證完畢!