在分散式系統架構中,數據是核心資產。無論是微服務還是傳統 SOA,服務之間的協作本質上都是數據的流轉。然而,異構平台(如 .NET、Java、Node.js)對數據的內部表現形式截然不同。為了讓雙方能「聽懂」彼此的數據,必須約定一套通用的表達格式——XML 正是業界公認的最佳選擇。WCF 的序列化機制,解決的正是將 .NET 物件轉換為 XML(以及反向操作)的標準化問題。本文將深入剖析 WCF 序列化的基石:數據契約與 DataContractSerializer。
正如服務契約(ServiceContract)抽象了服務操作,數據契約(DataContract)則抽象了交換的數據結構。它讓客戶端與服務端在無需共享程式集的情況下,對數據結構達成共識。WCF 採用屬性標註的方式定義數據契約,核心屬性有兩個:DataContractAttribute 與 DataMemberAttribute。
僅能套用於 類別、結構、列舉,不可用於介面;不可繼承,子類別必須顯式標註才能成為數據契約;每個類型僅能套用一次。
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class DataContractAttribute : Attribute
{
public bool IsReference { get; set; } // 是否保留物件引用結構
public string Name { get; set; } // 契約名稱(預設為類型名)
public string Namespace { get; set; } // 命名空間(預設為 https://schemas.datacontract.org/2004/07/{CLR命名空間})
}
IsReference 控制序列化時是否保留物件圖中的引用關係。例如兩個屬性指向同一個物件,啟用後 XML 會輸出引用 ID,否則會輸出兩份重複內容。
只有標註此屬性的欄位或屬性才會參與序列化,與存取修飾詞無關——私有成員也能成為數據成員。
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class DataMemberAttribute : Attribute
{
public string Name { get; set; } // 序列化後的元素名稱(預設為成員名)
public int Order { get; set; } // XML 中出現順序(預設 -1,父類別優先,同類別按字母序)
public bool IsRequired { get; set; } // 是否為必填(預設 false)
public bool EmitDefaultValue { get; set; } // 值為預設值時是否仍序列化(預設 true)
}
WCF 內建的預設序列化器,繼承自 XmlObjectSerializer。核心方法:WriteObject(序列化)、ReadObject(反序列化)。關鍵屬性解析:
IDataContractSurrogate,可在序列化/反序列化、匯入匯出契約時進行型別替換。IExtensibleDataObject 解決版本不一致導致的數據遺失(Round-trip)。DataContractAttribute.IsReference 同義,控制引用結構保留。透過範例觀察預設行為與自訂屬性的效果差異。
[DataMember] 的成員(如 TotalPrice 會被忽略)。範例輸出 XML 片段:
<Order xmlns:i="..." xmlns="https://schemas.datacontract.org/2004/07/Artech.DataContractSerializerDemos">
<Customer>NCS</Customer>
<Date>2008-12-03T00:00:00+08:00</Date>
<ID>5fdbee36-e29e-48d2-b45f-6fd4beba54d6</ID>
<ShipAddress>#328, Airport Rd...</ShipAddress>
<PaymentType>Credit Card</PaymentType>
</Order>
調整 Name、Namespace、Order 後,XML 變為:
<Ord xmlns:i="..." xmlns="https://www.artech.com/">
<OrderID>ba3bc051-6c02-41dd-9f97-ae745ac5f1dd</OrderID>
<OrderDate>2008-12-03T00:00:00+08:00</OrderDate>
<Customer>NCS</Customer>
<ShipAddress>#328, Airport Rd...</ShipAddress>
<PaymentType>Credit Card</PaymentType>
</Ord>
惡意客戶端發送超大集合導致伺服器資源耗盡。設定 MaxItemsInObjectGraph 可限制序列化/反序列化的最大物件數。計算規則:根物件 + 所有成員(含巢狀成員)各算一個。例如包含 10 個 Order(每個 4 屬性)的集合,總計 1 + 10×(1+4) = 51 個節點。超過限制即拋出 SerializationException。
可透過 ServiceBehaviorAttribute 或配置檔 <dataContractSerializer maxItemsInObjectGraph="..." /> 設定。
預設 false,多個屬性引用同一物件時會序列化為重複 XML 片段。設為 true(或在型別上標註 [DataContract(IsReference=true)])後,XML 會使用 z:Id 與 z:Ref 維持引用關係:
<Customer ... z:Id="1" xmlns:z="https://schemas.microsoft.com/2003/10/Serialization/">
<CompanyAddress z:Id="2">...</CompanyAddress>
<ShipAddress z:Ref="2" i:nil="true" />
</Customer>
這對於處理循環引用、共享子物件的場景至關重要。
數據契約與 DataContractSerializer 構成了 WCF 跨平台數據交換的基石。掌握屬性配置、序列化規則、安全限制與引用保留機制,能讓你精準控制 XML 輸出、防範攻擊、正確處理複雜物件圖,是構建健壯 WCF 服務的必修課。