首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WCF 技術深度剖析(十二):數據契約與 DataContractSerializer 詳解

     編輯:關於.NET

前言:分散式系統中的數據交換核心

在分散式系統架構中,數據是核心資產。無論是微服務還是傳統 SOA,服務之間的協作本質上都是數據的流轉。然而,異構平台(如 .NET、Java、Node.js)對數據的內部表現形式截然不同。為了讓雙方能「聽懂」彼此的數據,必須約定一套通用的表達格式——XML 正是業界公認的最佳選擇。WCF 的序列化機制,解決的正是將 .NET 物件轉換為 XML(以及反向操作)的標準化問題。本文將深入剖析 WCF 序列化的基石:數據契約與 DataContractSerializer。

一、數據契約:定義數據交換的「通用語言」

正如服務契約(ServiceContract)抽象了服務操作,數據契約(DataContract)則抽象了交換的數據結構。它讓客戶端與服務端在無需共享程式集的情況下,對數據結構達成共識。WCF 採用屬性標註的方式定義數據契約,核心屬性有兩個:DataContractAttribute 與 DataMemberAttribute。

1. DataContractAttribute:標記數據契約類型

僅能套用於 類別、結構、列舉,不可用於介面;不可繼承,子類別必須顯式標註才能成為數據契約;每個類型僅能套用一次。

[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,否則會輸出兩份重複內容。

2. DataMemberAttribute:顯式宣告數據成員

只有標註此屬性的欄位或屬性才會參與序列化,與存取修飾詞無關——私有成員也能成為數據成員。

[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)
}

二、DataContractSerializer:預設序列化引擎

WCF 內建的預設序列化器,繼承自 XmlObjectSerializer。核心方法:WriteObject(序列化)、ReadObject(反序列化)。關鍵屬性解析:

  • DataContractSurrogate:實作 IDataContractSurrogate,可在序列化/反序列化、匯入匯出契約時進行型別替換。
  • IgnoreExtensionDataObject:配合 IExtensibleDataObject 解決版本不一致導致的數據遺失(Round-trip)。
  • KnownTypes:預先註冊可能出現的衍生型別,確保反序列化能正確實例化具體類型。
  • MaxItemsInObjectGraph:防禦 DoS 攻擊,限制物件圖最大節點數(預設 65536)。
  • PreserveObjectReferences:與 DataContractAttribute.IsReference 同義,控制引用結構保留。

三、序列化規則實戰演示

透過範例觀察預設行為與自訂屬性的效果差異。

1. 預設序列化規則

  • 根元素名稱 = 類型名稱;命名空間採用預設格式。
  • 僅序列化標註 [DataMember] 的成員(如 TotalPrice 會被忽略)。
  • 所有成員均以 XML 元素輸出(非屬性)。
  • 順序規則:父類別成員優先 → 子類別成員;同層級按字母排序。

範例輸出 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>

2. 透過屬性自訂 XML 結構

調整 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:防禦拒絕服務攻擊

惡意客戶端發送超大集合導致伺服器資源耗盡。設定 MaxItemsInObjectGraph 可限制序列化/反序列化的最大物件數。計算規則:根物件 + 所有成員(含巢狀成員)各算一個。例如包含 10 個 Order(每個 4 屬性)的集合,總計 1 + 10×(1+4) = 51 個節點。超過限制即拋出 SerializationException。

可透過 ServiceBehaviorAttribute 或配置檔 <dataContractSerializer maxItemsInObjectGraph="..." /> 設定。

五、PreserveObjectReferences:保留物件引用結構

預設 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 服務的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved