閱讀852 返回首頁    go 小米 go 小米6


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

如果一個類型,不一定是數據契約,和給定的數據契約具有很大的差異,而我們要將該類型的對象序列化成基於數據契約對應的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 = "https://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="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://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)

WCF技術剖析係列:

WCF技術剖析之一:通過一個ASP.NET程序模擬WCF基礎架構
WCF技術剖析之二:再談IIS與ASP.NET管道
WCF技術剖析之三:如何進行基於非HTTP的IIS服務寄宿
WCF技術剖析之四:基於IIS的WCF服務寄宿(Hosting)實現揭秘
WCF技術剖析之五:利用ASP.NET兼容模式創建支持會話(Session)的WCF服務
WCF技術剖析之六:為什麼在基於ASP.NET應用寄宿(Hosting)下配置的BaseAddress無效
WCF技術剖析之七:如何實現WCF與EnterLib PIAB、Unity之間的集成
WCF技術剖析之八:ClientBase<T>中對ChannelFactory<T>的緩存機製
WCF技術剖析之九:服務代理不能得到及時關閉會有什麼後果?
WCF技術剖析之十:調用WCF服務的客戶端應該如何進行異常處理

WCF技術剖析之十一:異步操作在WCF中的應用(上篇)
WCF技術剖析之十一:異步操作在WCF中的應用(下篇)
WCF技術剖析之十二:數據契約(Data Contract)和數據契約序列化器(DataContractSerializer)
WCF技術剖析之十三:序列化過程中的已知類型(Known Type)
WCF技術剖析之十四:泛型數據契約和集合數據契約(上篇)
WCF技術剖析之十四:泛型數據契約和集合數據契約(下篇)
WCF技術剖析之十五:數據契約代理(DataContractSurrogate)在序列化中的作用
WCF技術剖析之十六:數據契約的等效性和版本控製



作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 12:04:19

  上一篇:go  WCF技術剖析之十四:泛型數據契約和集合數據契約(下篇)
  下一篇:go  WCF技術剖析之十六:數據契約的等效性和版本控製