程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF技術剖析之十五:數據契約代理(DataContractSurrogate)在序列化中的作用

WCF技術剖析之十五:數據契約代理(DataContractSurrogate)在序列化中的作用

編輯:關於.NET

如果一個類型,不一定是數據契約,和給定的數據契約具有很大的差異,而我們要將該類型的對象序列化成基於數據契約對應的XML。反之,對於一段給定的基於數據契約的XML,要通過反序列化生成該類型的對象,我們該如何實現這樣的場景?

比如下面定義了兩個類型Contact和Customer,其中Customer是數據契約,Contact的Sex屬性相當於Customer的Gender屬性,而Contact的FullName可以看成是Customer的FirstName和LastName的組合。現在我們要做的是將一個Contact對象序列化成基於Customer數據契約對應的結構的XML,或者對於一段基於Customer數據契約對應結構的XML,將其反序列化生成Contact對象。

   1: public class Contact
2: {
3: public string FullName
4: { get; set; }
5:
6: public string Sex
7: { get; set; }
8:
9: public override bool Equals(object obj)
10: {
11: Contact contact = obj as Contact;
12: if (contact == null)
13: {
14: return false;
15: }
16:
17: return this.FullName == contact.FullName && this.Sex == contact.Sex;
18: }
19:
20: public override int GetHashCode()
21: {
22: return this.FullName.GetHashCode() ^ this.Sex.GetHashCode();
23: }
24: }
1: [DataContract(Namespace = "http://www.artech.com")]
2: public class Customer
3: {
4: [DataMember(Order = 1)]
5: public string FirstName
6: { get; set; }
7:
8: [DataMember(Order = 2)]
9: public string LastName
10: { get; set; }
11:
12: [DataMember(Order = 3)]
13: public string Gender
14: { get; set; }
15: }

為實現上面的要求,要涉及WCF中一個特殊的概念:數據契約代理(DataContract Surrogate)。WCF通過一個接口System.Runtime.Serialization.IDataContractSurrogate來表示數據契約代理。IDataContractSurrogate用於實現在序列化、反序列化、數據契約的導入和導出過程中對對象或者類型的替換。以上面Contact和Customer為例,在正常的情況下,DataContractSerializer針對類型Customer對一個真正的Customer對象進行序列化,現在要求的是通過DataContractSerializer序列化一個Contact對象,並且要生成與Customer等效的XML,就要在序列化的過程中實現類型的替換(由Contact類型替換成Customer類型)和對象的替換(由Contact對象替換成Customer對象)。

我們先來看看IDataContractSurrogate的定義,序列化相關的方法有以下3個,如果想具體了解IDataContractSurrogate在數據契約導入、導出,以及代碼生成方面的應用可以參考MSDN相關文檔,在這裡就不多作介紹了。

GetDataContractType:獲取進行序列化、反序列化或者數據契約導入導出基於的數據契約的類型,實現此方法相當於實現了類型的替換;

GetObjectToSerialize:在序列化之前獲取序列化的對象,實現了此方法相當於為序列化實現了對象替換;

GetDeserializedObject:當完成反序列化工作後,通過方法獲得被反序列化生成的對象,通過此方法可以用新的對象替換掉真正被反序列化生成的原對象。

   1: public interface IDataContractSurrogate
2: {
3: Type GetDataContractType(Type type);
4: object GetObjectToSerialize(object obj, Type targetType);
5: object GetDeserializedObject(object obj, Type targetType);
6:
7: object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType);
8: object GetCustomDataToExport(Type clrType, Type dataContractType);
9: void GetKnownCustomDataTypes(Collection<Type> customDataTypes);
10: Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData);
11: CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit);
12: }

現在我專門為Contact和Customer之間的轉換創建了一個自定義的DataContractSurrogate:ContractSurrogate。在GetDataContractType中,如果發現類型是Contact,則替換成Customer。在GetObjectToSerialize方法中,將用於序列化的Contact對象用Customer對象替換,而在GetDeserializedObject中則用Contact對象替換反序列化生成的Customer對象。

   1: public class ContractSurrogate : IDataContractSurrogate
2: {
3:
4: public object GetCustomDataToExport(Type clrType, Type dataContractType)
5: {
6: return null;
7: }
8:
9: public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
10: {
11: return null;
12: }
13:
14: public Type GetDataContractType(Type type)
15: {
16: if (type == typeof(Contact))
17: {
18: return typeof(Customer);
19: }
20:
21: return type;
22: }
23:
24: public object GetDeserializedObject(object obj, Type targetType)
25: {
26: Customer customer = obj as Customer;
27: if (customer == null)
28: {
29: return obj;
30: }
31:
32: return new Contact
33: {
34: FullName = string.Format("{0} {1}", customer.FirstName, customer.LastName),
35: Sex = customer.Gender
36: };
37: }
38:
39: public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
40: {
41:
42: }
43:
44: public object GetObjectToSerialize(object obj, Type targetType)
45: {
46: Contact contact = obj as Contact;
47: if (contact == null)
48: {
49: return obj;
50: }
51:
52:
53: return new Customer
54: {
55: FirstName = contact.FullName.Split(" ".ToCharArray())[0],
56: LastName = contact.FullName.Split(" ".ToCharArray())[1],
57: Gender = contact.Sex
58: };
59: }
60:
61: public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
62: {
63: return null;
64: }
65:
66: public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
67: {
68: return typeDeclaration;
69: }
70: }

為了演示ContractSurrogate在序列化和反序列化中所起的作用,創建了Serialize<T>和Deserialize<T>兩個輔助方法,通過創建DataContractSerializer進行序列化和反序列化。方法中的dataContractSurrogate參數被傳入DataContractSerializer的構造函數中。

   1: public static void Serialize<T>(T instance, string fileName, IDataContractSurrogate dataContractSurrogate)
2: {
3: DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, false, dataContractSurrogate);
4: using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
5: {
6: serializer.WriteObject(writer, instance);
7: Process.Start(fileName);
8: }
9: }
10:
11: public static T Deserialize<T>( string fileName, IDataContractSurrogate dataContractSurrogate)
12: {
13: DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, false, dataContractSurrogate);
14: using (XmlReader reader = new XmlTextReader(fileName))
15: {
16: return (T)serializer.ReadObject(reader);
17: }
18: }

借助於上面定義的ContractSurrogate和兩個輔助方法,我們通過下面的程序演示IDataContractSurrogate在序列化和反序列化過程中所起的作用。

   1: string fileName = @"E:\contact.xml";
2: Contact contactToSerialize = new Contact
3: {
4: FullName = "Bill Gates",
5: Sex = "Male"
6: };
7: IDataContractSurrogate dataContractSurrogate = new ContractSurrogate();
8: Serialize<Contact>(contactToSerialize, fileName, dataContractSurrogate);
9:
10: Contact contactToDeserialize = Deserialize<Contact>(fileName, dataContractSurrogate);
11: Console.WriteLine("contactToSerialize.Equals(contactToDeserialize) = {0}", contactToSerialize.Equals(contactToDeserialize) );

下面的XML表述Contract對象被序列化後的結果,顯而易見,這和真正序列化一個Customer對象是完全一樣的。不僅如此,基於下面一段XML反序列化生成的Contact對象和用於序列化的對象是相等的,這通過最終的輸出結果可以看出來。

   1: <Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com">
2: <FirstName>Bill</FirstName>
3: <LastName>Gates</LastName>
4: <Gender>Male</Gender>
5: </Customer>

輸出結果:

   1: contactToSerialize.Equals(contactToDeserialize) = True

在進行服務寄宿的時候,可以通過如下代碼指定IDataContractSurrogate。

   1: using (ServiceHost serviceHost = new ServiceHost(typeof(InventoryCheck)))
2: foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints)
3: {
4: foreach (OperationDescription op in ep.Contract.Operations)
5: {
6: DataContractSerializerOperationBehavior dataContractBehavior =
7: op.Behaviors.Find<DataContractSerializerOperationBehavior>()
8: as DataContractSerializerOperationBehavior;
9: if (op.Behaviors.Find<DataContractSerializerOperationBehavior>()
10: != null)
11: dataContractBehavior.DataContractSurrogate = new ContractSurrogate();
12: op.Behaviors.Add(op.Behaviors.
13: Find<DataContractSerializerOperationBehavior>());
14:
15: dataContractBehavior = new DataContractSerializerOperationBehavior(op);
16: dataContractBehavior.DataContractSurrogate =
17: new ContractSurrogate ();
18: op.Behaviors.Add(dataContractBehavior);
19: }
20: }

注:部分內容節選自《WCF技術剖析(卷1)》第五章:序列化與數據契約(Serialization and Data Contract)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved