學藝不精,陸續踩了幾次雷,整理DataContract與DataMember對序列化的影響備忘。

WCF預設使用DataContractSerializer執行序列化,而DataContractSerializer可依物件類別標註的[DataContract]及[DataMember]、[IgnoreDataMember]等Attribute決定哪些屬性該序列化。依照MSDN文件,DataContractSerializer的處理原則為:

  1. 類別標示[DataContract]時,只序列化有標示[DataMember]的項目
    註:DataMember不限於public Property,可用於各種存取層級(public、private、internal、protected)的Property或Field。
  2. 若類別未標示[DataContract],DataContractSerializer預設會序列化「所有可讀寫的public Property及Field」,此時可透過[IgnoreDataMember]負向表列不要序列化的Property或Field。

我設計了以下幾個型別來觀察DataContractSerializer行為,有Property有Field,有public有private,再配合[DataContract]、[DataMember]、[IgnoreDataMember]組合出不同變化:

using System.Runtime.Serialization;
 
[DataContract]
public class Blah1
{
    public int PropNoAttr { get; set; }
    public int Field;
    private int PrivProp { get; set; }
    private int PrivField;
 
}
 
[DataContract]
public class Blah2
{
    public int PropNoAttr { get; set; }
    [DataMember]
    public int PropAttr { get; set; }
    public int Field;
    [DataMember]
    private int PrivProp { get; set; }
    [DataMember]
    private int PrivField;
 
}
 
[DataContract]
public class Blah3
{
    public int PropNoAttr { get; set; }
    [IgnoreDataMember]
    public int PropIgnoreAttr { get; set; }
    public int Field;
    private int PrivProp { get; set; }
    private int PrivField;
 
}
 
 
public class Blah4
{
    public int PropNoAttr { get; set; }
    [DataMember]
    public int PropAttr { get; set; }
    [IgnoreDataMember]
    public int PropIgnoreAttr { get; set; }
 
    public int Field;
    private int PrivProp { get; set; }
    private int PrivField;
}
 
public class Blah5
{
    public int PropNoAttr { get; set; }
    public int ReadonlyProp { get; }
    public int Field;
    private int PrivProp { get; set; }
    private int 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
    {
        static void 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();
        }
        static string 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
=====================================
<Blah1 xmlns="http://schemas.datacontract.org/2004/07/" 
  xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
 
Blah2
=====================================
<Blah2 xmlns="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
=====================================
<Blah3 xmlns="http://schemas.datacontract.org/2004/07/" 
  xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
 
Blah4
=====================================
<Blah4 xmlns="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
=====================================
<Blah5 xmlns="http://schemas.datacontract.org/2004/07/" 
  xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Field>0</Field>
  <PropNoAttr>0</PropNoAttr>
</Blah5>
  1. Blah1
    只標了[DataContract]沒標任何[DataMember],XML空空如也,沒有任何Property或Field被序列化
  2. Blah2
    標註[DataContract]後,所有標註[DataMember]的成員都包含在XML中,不論public或private,不管是Property或是Field
  3. Blah3
    標註[DataContract]後DataContractSerializer只認[DataMember]辦事,[IgnoreDataMember]沒有作用,XML是空的
  4. Blah4
    未標註[DataContract],除了[IgnoreDataMember] PropIgnoreAttr外,所有public的Property及Field都被序列化
  5. Blah5
    未加任何Attribute,DataContractSerializer將序列化所有公開Property及Field,但Property限定可讀寫者,刻意放了一個public ReadonlyProp { get; }唯讀屬性,果然被排除了。

實驗驗證完畢!


Comments

# by Jonh doe

最後一個blah4 應該是blah5

# by Jeffrey

to John, 一如往例,沒有錯字就不是黑大真跡 orz orz orz。謝謝指正。

# by Carter

Property 似乎不能用 readonly

Post a comment