Windows Communication Foundation (WCF) 資料契約模型支援直接代表 XML 的特定類型。 當這些類型串行化為 XML 時,串行化程式會寫出這些類型的 XML 內容,而不需要進一步處理。 支援的型別為 XmlElement、 XmlNode 陣列(但不是 XmlNode 型別本身),以及實 IXmlSerializable作的型別。
DataSet和 DataTable 型別以及具型別數據集通常用於資料庫程序設計。 這些類型會實作 IXmlSerializable 介面,因此可在數據合約模型中串行化。 本主題結尾會列出這些類型的一些特殊考慮。
XML 類型
Xml 元素
XmlElement 類型會使用其 XML 內容進行序列化。 例如,使用下列類型。
[DataContract(Namespace=@"http://schemas.contoso.com")]
public class MyDataContract
{
[DataMember]
public XmlElement myDataMember;
public void TestClass()
{
XmlDocument xd = new XmlDocument();
myDataMember = xd.CreateElement("myElement");
myDataMember.InnerText = "myContents";
myDataMember.SetAttribute
("myAttribute","myValue");
}
}
<DataContract([Namespace]:="http://schemas.contoso.com")> _
Public Class MyDataContract
<DataMember()> _
Public myDataMember As XmlElement
Public Sub TestClass()
Dim xd As New XmlDocument()
myDataMember = xd.CreateElement("myElement")
myDataMember.InnerText = "myContents"
myDataMember.SetAttribute("myAttribute", "myValue")
End Sub
End Class
這會串行化為 XML,如下所示:
<MyDataContract xmlns="http://schemas.contoso.com">
<myDataMember>
<myElement xmlns="" myAttribute="myValue">
myContents
</myElement>
</myDataMember>
</MyDataContract>
請注意,包裝函式數據成員元素 <myDataMember> 仍然存在。 在資料合約模型中無法移除這個元素。 處理此模型(DataContractSerializer 和 NetDataContractSerializer)的序列化器可能會在這個包裝元素中發出特殊屬性。 這些屬性包括標準 XML 架構實例 「nil」 屬性(允許 XmlElement 為 null)和 「type」 屬性(允許 XmlElement 多型使用)。 此外,下列 XML 屬性是 WCF 特有的:“Id”、“Ref”、“Type” 和 “Assembly”。 這些屬性可能會生成,以支援在啟用物件圖保留模式時使用 XmlElement,或是使用 NetDataContractSerializer。 (如需物件圖形保留模式的詳細資訊,請參閱 串行化和還原串行化。)
允許使用XmlElement陣列或集合,且它們的處理方式與其他陣列或集合相同。 也就是說,整個集合都有一個包裝元素,並且數組中的每個<myDataMember>都有一個獨立的包裝元素,類似於XmlElement在前面範例中的用法。
在反序列化過程中,反序列化器從傳入的 XML 建立一個 XmlElement。 反序列化器提供了有效的父代 XmlDocument。
請確定還原串行化為 的 XmlElement XML 片段會定義它使用的所有前置詞,而且不依賴上階元素的任何前置詞定義。 只有當使用 DataContractSerializer 從不同的 (非DataContractSerializer) 來源存取 XML 時,才會擔心這個問題。
搭配 DataContractSerializer使用 時, XmlElement 可以多型方式指派 ,但只能指派給 類型的 Object數據成員。 即使實作 IEnumerable, XmlElement 也無法當做集合型別使用 ,也無法指派給 IEnumerable 數據成員。 如同所有多型的賦值,DataContractSerializer 所產生的 XML 會包含數據合約名稱。 在此情況下,它是命名空間中的 http://schemas.datacontract.org/2004/07/System.Xml 「XmlElement」。
使用NetDataContractSerializer時,支援將XmlElement有效地指派到Object或IEnumerable的任何多型指派。
請勿嘗試將衍生自XmlElement的類型搭配其中任一序列化器使用,無論是否以多型方式分配。
XmlNode 的陣列
使用的陣列 XmlNode 非常類似於使用 XmlElement。 使用的陣列 XmlNode 比使用 XmlElement更有彈性。 您可以在資料成員包裝項目內寫入多個元素。 您還可以在數據成員包裹元素內插入除元素外的內容,例如 XML 註解。 最後,您可以將屬性放入包裝數據成員元素中。 這一切可以藉由將XmlNode的陣列填入XmlNode的特定衍生類別,例如XmlAttribute、XmlElement或XmlComment來實現。 例如,使用下列類型。
[DataContract(Namespace="http://schemas.contoso.com")]
public class MyDataContract
{
[DataMember]
public XmlNode[] myDataMember = new XmlNode[4];
public void TestClass()
{
XmlDocument xd = new XmlDocument();
XmlElement xe = xd.CreateElement("myElement");
xe.InnerText = "myContents";
xe.SetAttribute
("myAttribute","myValue");
XmlAttribute atr = xe.Attributes[0];
XmlComment cmnt = xd.CreateComment("myComment");
myDataMember[0] = atr;
myDataMember[1] = cmnt;
myDataMember[2] = xe;
myDataMember[3] = xe;
}
}
<DataContract([Namespace]:="http://schemas.contoso.com")> _
Public Class MyDataContract
<DataMember()> _
Public myDataMember(3) As XmlNode
Public Sub TestClass()
Dim xd As New XmlDocument()
Dim xe As XmlElement = xd.CreateElement("myElement")
xe.InnerText = "myContents"
xe.SetAttribute("myAttribute", "myValue")
Dim atr As XmlAttribute = xe.Attributes(0)
Dim cmnt As XmlComment = xd.CreateComment("myComment")
myDataMember(0) = atr
myDataMember(1) = cmnt
myDataMember(2) = xe
myDataMember(3) = xe
End Sub
End Class
串行化時,產生的 XML 類似於下列程式代碼。
<MyDataContract xmlns="http://schemas.contoso.com">
<myDataMember myAttribute="myValue">
<!--myComment-->
<myElement xmlns="" myAttribute="myValue">
myContents
</myElement>
<myElement xmlns="" myAttribute="myValue">
myContents
</myElement>
</myDataMember>
</MyDataContract>
請注意,數據成員包裝元件 <myDataMember> 包含屬性、註解和兩個元素。 這些是已串行化的四 XmlNode 個實例。
無效的 XML 不能進行序列化,因為 XmlNode 陣列會導致這種情況。 例如,兩個實例的 XmlNode 陣列,其中第一個實例是 , XmlElement 而第二個實例是 XmlAttribute 無效的,因為這個序列不會對應至任何有效的 XML 實例(沒有附加屬性的位置)。
在反序列化XmlNode陣列時,會建立節點,並填入來自傳入 XML 的資訊。 反序列化器提供了有效的父代 XmlDocument。 所有節點都會還原串行化,包括包裝函式數據成員元素上的任何屬性,但不包括 WCF 串行化程式放置於該處的屬性(例如用來表示多型指派的屬性)。 在 XML 片段中定義所有命名空間前置詞的警告適用於還原序列化 XmlNode 陣列,就像它適用於還原序列化 XmlElement 一樣。
在開啟物件關係保留的情況下使用序列化器時,物件的相等性只會在 XmlNode 陣列層級上保留,而不是個別 XmlNode 實例。
請勿嘗試將陣列XmlNode序列化,其中一或多個節點已設定為null。 允許整個陣列成員為 null,但不允許陣列中包含的任何單個 XmlNode。 如果整個數位成員為 null,包裝函式數據成員元素會包含特殊屬性,指出它是 Null。 在還原序列化時,整個陣列成員也會變成 null。
只有 XmlNode 的一般陣列會被串行化程式特別處理。 宣告為包含 XmlNode 的其他集合型別的資料成員或是宣告為衍生自 XmlNode 型別的陣列的資料成員,將不會被特別處理。 因此,除非它們也符合串行化的其他其中一個準則,否則通常無法串行化。
允許使用數組或數組的集合 XmlNode。 整個集合有一個包裝元素,而外部陣列或集合中的每個<myDataMember>陣列都有個別的包裝元素(類似於上述範例中的XmlNode)。
將Array實例填入類型為Object的Array或類型為IEnumerable的XmlNode的資料成員,並不會導致該資料成員被視為Array的XmlNode實例。 每個陣列成員都會個別序列化。
使用 DataContractSerializer 時,可以將 XmlNode 陣列以多型方式指派,但只能指派至類型為 Object 的數據成員。 即使實作 IEnumerable,但的 XmlNode 陣列無法當做集合類型使用,並指派給 IEnumerable 數據成員。 如同所有多型指派一樣,DataContractSerializer 會在所產生的 XML 中發出數據合約名稱,本案例中,即是在命名空間 http://schemas.datacontract.org/2004/07/System.Xml 中的 "ArrayOfXmlNode"。 當與 NetDataContractSerializer 搭配使用時,支援任何有效的 XmlNode 陣列指派。
架構考慮
如需 XML 類型架構對應的詳細資訊,請參閱 數據合約架構參考。 本節提供重要要點的摘要。
XmlElement 類型的數據成員會對應至使用下列匿名類型所定義的元素。
<xsd:complexType>
<xsd:sequence>
<xsd:any minOccurs="0" processContents="lax" />
</xsd:sequence>
</xsd:complexType>
一個類型為 Array XmlNode 的數據成員會映射到使用下列匿名類型所定義的元素。
<xsd:complexType mixed="true">
<xsd:sequence>
<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
</xsd:sequence>
<xsd:anyAttribute/>
</xsd:complexType>
實作 IXmlSerializable 介面的類型
IXmlSerializable 介面所實現的類型完全受到 DataContractSerializer 的支援。 屬性 XmlSchemaProviderAttribute 應該一律套用至這些類型,以控制其架構。
實作的類型 IXmlSerializable有三個品種:代表任意內容類型的類型、代表單一元素的類型,以及舊版 DataSet 類型。
內容類型使用由屬性
XmlSchemaProviderAttribute指定的架構提供者方法。 方法不會傳回null,而且 IsAny 屬性上的 屬性會保留在其預設值false。 這是最常見的IXmlSerializable類型的用法。元素類型在
IXmlSerializable類型必須控制其自身根元素名稱時使用。 若要將類型標示為項目類型,請將 屬性上的 IsAny 屬性設定XmlSchemaProviderAttribute為true,或從架構提供者方法傳回 Null。 對於元素類型而言,是否具有架構提供者方法是可以選擇的 – 您可以在XmlSchemaProviderAttribute中指定為 null,而不是方法名稱。 不過,如果IsAny是true而且指定了架構提供者方法,則該方法必須傳回 null。舊版 DataSet 類型是未加上
IXmlSerializable屬性標記的XmlSchemaProviderAttribute類型。 相反地,它們會依賴 GetSchema 方法來產生架構。 此模式用於DataSet類型,且其具型別數據集會衍生舊版 .NET Framework 中的類別,但現在已過時,而且僅基於舊版的原因才支援。 請勿依賴此模式,且一律將 套用XmlSchemaProviderAttribute至您的IXmlSerializable類型。
IXmlSerializable 的內容類型
當序列化一個實作IXmlSerializable的類型並且是先前定義的內容類型的數據成員時,序列化程式會寫入數據成員的包裝元素,並將控制權傳遞至 WriteXml 方法。 實作WriteXml可以寫入任何 XML 格式,包括將屬性加入包裝元素。 完成WriteXml之後,串行化程式會關閉元素。
當反序列化實作了 IXmlSerializable 並且是之前定義的內容類型的數據成員時,反序列化程式會將 XML 讀取器定位在數據成員的包裝元素上,然後將控制權傳遞給 ReadXml 方法。 方法必須讀取整個元素,包括起始與結束標籤。 請確定您的程式 ReadXml 代碼會處理元素是空的案例。 此外,您的 ReadXml 實作不應依賴包覆元素被命名為特定方式。 串行化程式選擇的名稱可能會有所不同。
可以以多型方式將 IXmlSerializable 內容類型指派給類型為 Object 的數據成員。 也允許類型實例為 Null。 最後,可以使用啟用了物件圖形保留功能的IXmlSerializable型別和NetDataContractSerializer。 所有這些功能都需要 WCF 串行化程式將特定屬性(“nil”和“type”屬性在 XML 架構實例命名空間中,以及“Id”、“Ref”、“Type”和“Assembly”屬性在 WCF 特定命名空間中)附加到包裝元素中。
實作 ReadXml 時要忽略的屬性
在將控制權轉移給您的 ReadXml 程式碼之前,解串行化器會檢查 XML 元素,偵測到這些特殊的 XML 屬性後,會根據它們採取相應的動作。 例如,如果「nil」為true,則會反序列化為空值,而不會呼叫ReadXml。 如果偵測到多型,元素的內容會被反序列化,彷彿它是不同的類型。 呼叫多型分配型別的ReadXml實作。 在任何情況下,ReadXml 的實作都應該忽略這些特殊屬性,因為它們是由解序程序處理。
IXmlSerializable 內容類型的結構考量
匯出架構 IXmlSerializable 內容類型時,會呼叫架構提供者方法。 一個XmlSchemaSet被傳遞至一個架構提供者方法。 方法可以將任何有效的架構新增至架構集。 架構集包含架構匯出發生時已經知道的架構。 當架構提供者方法必須將項目加入架構集時,它必須判斷具有適當命名空間的 XmlSchema 是否已經存在於架構集中。 如果這樣做,架構提供者方法必須將新專案新增至現有的 XmlSchema。 否則,它必須建立新的 XmlSchema 實例。 zh-TW: 如果使用 IXmlSerializable 類型的陣列,這是很重要的。 例如,如果您的 IXmlSerializable 類型在命名空間 「B」 中匯出為類型 「A」,則當架構提供者方法呼叫架構集時,可能會包含 「B」 的架構來保存 「ArrayOfA」 類型的架構。
除了將型別加入至 XmlSchemaSet之外,內容類型的架構提供者方法必須傳回非空值。 它可以返回一個 XmlQualifiedName,指示要用於給定 IXmlSerializable 類型的架構類型名稱。 此限定名稱也會做為型別的數據合約名稱和命名空間。 當架構提供者方法傳回時,允許傳回架構集合中不存在的類型。 不過,預期在導出所有相關類型時,在 Export 上呼叫 XsdDataContractExporter 方法並存取 Schemas 屬性,該型別存在於架構集中。 在進行所有相關 Schemas 呼叫之前存取 Export 屬性可能會導致 XmlSchemaException。 如需匯出程式的詳細資訊,請參閱 從類別導出架構。
架構提供者方法也可以傳回要使用的 XmlSchemaType。 此類型不一定是匿名的。 如果它是匿名的,則每當IXmlSerializable型別被用作數據成員時,IXmlSerializable型別的架構都會匯出為匿名型別。 此 IXmlSerializable 類型仍然具有數據合約名稱和命名空間。 (此決定方式如 數據合約名稱 中所述,不同之處在於 DataContractAttribute 屬性無法用來自定義名稱。如果它不是匿名的,它必須是 中的 XmlSchemaSet其中一個型別。 此案例等同於將某類型的XmlQualifiedName傳回。
此外,類型的全域元素宣告會被匯出。 如果類型沒有 XmlRootAttribute 套用屬性,元素的名稱和命名空間與資料合約相同,而且其 「nillable」 屬性為 true。 唯一的例外是架構命名空間 (http://www.w3.org/2001/XMLSchema; 如果型別的數據合約在此命名空間中,對應的全域元素位於空白命名空間中,因為它禁止將新元素加入架構命名空間。 如果型別套用了 XmlRootAttribute 屬性,則會使用下列屬性匯出全域元素宣告:ElementName、Namespace和 IsNullable。 套用的 XmlRootAttribute 預設值是資料契約名稱、空命名空間,以及「nillable」為 true。
相同的全域元素宣告規則會套用至舊版數據集類型。 請注意,XmlRootAttribute無法覆寫透過自訂程式代碼新增的全域元素宣告,無論是使用架構提供者方法新增至 XmlSchemaSet ,還是透過舊版資料集類型新增至 GetSchema 。
IXmlSerializable 項目類型
IXmlSerializable 專案型別的 IsAny 屬性已設定為 true ,或讓其架構提供者方法傳回 null。
將項目類型串行化和還原串行化與將內容類型串行化和還原串行化非常相似。 不過,有一些重要的差異:
實作
WriteXml應該只撰寫一個元素(當然可以包含多個子元素)。 它不應該在單一元素、多個同層級元素或混合內容之外寫入屬性。 元素可能是空的。實作
ReadXml不應該讀取包裝元件。 預期會讀取WriteXml所產生的一個元素。定期序列化元素類型時(例如,作為資料合約中的資料成員形式),序列化器會在呼叫
WriteXml前輸出包裝元素,就像內容類型一樣。 不過,在最上層串行化元素類型時,除非在WriteXml或DataContractSerializer建構函式中構建串行化程式時明確指定了根名稱和命名空間,串行化程式通常不會在NetDataContractSerializer寫入的元素周圍輸出包裝元素。 如需詳細資訊,請參閱 串行化和還原串行化。在最上層串行化元素類型時,如果在建構時未指定根名稱和命名空間,WriteStartObject 和 WriteEndObject 將基本上不執行任何動作,而 WriteObjectContent 則會呼叫
WriteXml。 在此模式中,要串行化的物件不可以是 Null,而且無法以多型方式指派。 此外,無法啟用物件圖形保留,而且NetDataContractSerializer無法使用 。在反序列化最上層的元素類型時,如果未在建構階段指定根名稱和命名空間,IsStartObject 就會返回
true,前提是它能找到任何元素的開頭。 ReadObject 的行為方式在參數verifyObjectName設定為true時,與IsStartObject實際讀取物件之前相同。ReadObject然後將控件傳遞至ReadXml方法。
針對元素型別導出的架構與上一節中描述的 XmlElement 型別相同,不同之處在於架構提供者方法可以像針對內容類型一樣將任何其他架構新增至 XmlSchemaSet。
XmlRootAttribute 屬性不允許搭配元素類型使用,且永遠不會針對這些類型發出全域元素宣告。
與 XmlSerializer 的不同之處
介面 IXmlSerializable 和 XmlSchemaProviderAttribute 和 XmlRootAttribute 屬性也由 XmlSerializer 瞭解。 不過,在數據合約模型中,這些處理方式有一些差異。 下列摘要說明重要差異:
架構提供者方法必須是公用的,才能在
XmlSerializer中使用,但不必是公用的,也可以在數據合約模型中使用。當資料契約模型中的
IsAny為 true,而不是在XmlSerializer的情況下,會呼叫架構提供者方法。當內容或舊版數據集類型沒有
XmlRootAttribute屬性時,XmlSerializer會在空白命名空間中匯出全域元素宣告。 在數據合約模型中,使用的命名空間通常是數據合約命名空間,如先前所述。
在建立與這兩種串行化技術搭配使用的型別時,請注意這些差異。
匯入 IXmlSerializable 架構
從 IXmlSerializable 類型生成的架構匯入時,會有幾個可能性:
產生的架構可能是有效的數據合約架構,如 數據合約架構參考中所述。 在此情況下,可以如往常匯入架構,併產生一般數據合約類型。
產生的架構可能不是有效的數據合約架構。 例如,您的架構提供者方法可能會產生包含數據合約模型中不支援之 XML 屬性的架構。 在此情況下,您可以將架構匯入為
IXmlSerializable類型。 預設不會開啟此匯入模式,但很容易啟用 –例如,/importXmlTypes使用命令行參數切換至 ServiceModel 元數據公用程式工具 (Svcutil.exe) 。 這在匯入模式以產生類別中詳細說明。 請注意,您必須直接使用類型實例的 XML。 您也可以考慮使用不同的串行化技術,以支援更廣泛的架構 – 請參閱使用XmlSerializer主題。您可能想要在 Proxy 中重複使用現有的
IXmlSerializable類型,而不是產生新的類型。 在此情況下,匯入架構至產生類型主題中所述的參考型別功能可用來指出要重複使用的類型。 這對應於在/referencesvcutil.exe上使用 開關,該開關會指定包含可再次使用之類型的組件。
代表數據合約中的任意 XML
XmlElement、XmlNode 和 IXmlSerializable 類型的陣列允許您將任意 XML 插入資料合約模型。
DataContractSerializer 和 NetDataContractSerializer 將這個 XML 內容傳遞至所使用的 XML 寫入器,而不會干擾過程。 不過,XML 寫入器可能會對所寫入的 XML 強制執行特定限制。 具體來說,以下是一些重要的範例:
XML 寫入器通常不允許在撰寫另一份檔案中間的 XML 檔案宣告(例如 <?xml version='1.0' ?>)。 您無法取得完整的 XML 檔,並將它串行化為
ArrayXmlNode資料成員。 若要這樣做,您必須移除檔宣告,或使用您自己的編碼配置來表示它。WCF 提供的所有 XML 寫入器都會拒絕 XML 處理指令 (<? ... ?>) 和檔案類型定義 (<! ... ... >),因為它們不允許在 SOAP 訊息中。 同樣地,您可以使用自己的編碼機制來繞過這項限制。 如果您必須將這些專案包含在產生的 XML 中,您可以撰寫使用支援這些 XML 寫入器的自訂編碼器。
實作
WriteXml時,請避免在 XML 寫入器上呼叫 WriteRaw 方法。 WCF 會使用各種不同的 XML 編碼方式(包括二進位),因此很難或不可能使用WriteRaw,如此一來,任何編碼方式都可使用結果。實作
WriteXml時,請避免使用 WriteEntityRef 和 WriteNmToken 方法,這些方法不被 WCF 所提供的 XML 寫入器支援。
使用 DataSet、類型化的 DataSet 和 DataTable
數據合約模型中完全支援使用這些類型。 使用這些類型時,請考慮下列幾點:
這些類型的架構(特別是 DataSet 其具型別的衍生類別)可能無法與某些非 WCF 平臺互通,或在搭配這些平臺使用時,可能會導致使用能力不佳。 此外,使用
DataSet型別可能會對效能造成影響。 最後,它可能會讓您在未來更難建立應用程式版本。 請考慮使用明確定義的資料契約類型,而不是契約中的DataSet類型。匯入
DataSet或DataTable架構時,請務必參考這些類型。 使用 Svcutil.exe 命令行工具,即可將 System.Data.dll 元件名稱傳遞至/reference選項來完成。 如果匯入具類型的數據集架構,您必須參考具類型的數據集類型。 使用 Svcutil.exe,將具型別數據集元件的位置傳遞至/reference開關。 如需參考型別的詳細資訊,請參閱 匯入架構以產生類別。
在數據合約模型中,對具類型化的 DataSet 的支援有限。 具類型的數據集可以串行化和還原串行化,而且可以匯出其架構。 不過,數據合約架構匯入無法從架構產生新的具型別 DataSet 類型,因為它只能重複使用現有的數據類型。 您可以使用/r選項在Svcutil.exe中指向現有的具型別DataSet。 如果您嘗試在使用具型別數據集的服務上,沒有使用 /r 開關的情況下使用 Svcutil.exe,則會自動選取替代的序列化程序(XmlSerializer)。 如果您必須使用 DataContractSerializer 並需要從架構產生 DataSets,您可以使用以下步驟:首先使用 Xsd.exe 工具結合 /d 參數在服務上產生具型別的 DataSet 類型,接著編譯這些類型,最後使用 /r 參數在 Svcutil.exe上指向這些類型。
另請參閱
- DataContractSerializer
- IXmlSerializable
- 使用數據合約
- 數據合約串行化程式 支援的 類型