[原创]WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

来源:互联网 发布:沪深300指数算法 编辑:程序博客网 时间:2024/06/05 08:31

[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的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)

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技术剖析之十六:数据契约的等效性和版本控制


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鼻炎犯了鼻子不通气怎么办 食物呛到鼻子里怎么办 胃疼引起的焦虑怎么办 泰迪犬发生口腔亏痒怎么办? 俩鼻子都堵了怎么办 2岁宝宝鼻甲肿大怎么办 感冒10多天不好怎么办 宝妈鼻炎犯了怎么办 鼻炎犯了好多黄鼻涕怎么办 空调吹多了上火怎么办 宝宝感冒鼻塞流鼻涕口臭怎么办 怀孕了有鼻息肉怎么办 孕晚期鼻炎犯了怎么办 孕妇鼻炎犯了头疼怎么办 一岁宝宝有鼻炎怎么办 蒸馒头的面酸了怎么办 dnf点券充错账号了怎么办 英雄联盟点券充错区了怎么办 文玩鼻烟壶盖子松了怎么办 文胸磨腋下的肉怎么办 荷兰在窝里拉屎怎么办 宝宝把泡沫吃了怎么办 荷兰猪一直叫该怎么办 人吃了缓冲泡沫怎么办 不小心吃了泡沫怎么办 不想养荷兰猪了怎么办 刚买的乌龟死了怎么办 剪猫指甲出血了怎么办 猫吃了酸性植物怎么办 金鱼在缸底不动怎么办 野兔子不吃不喝怎么办 让荷兰猪咬了怎么办 仓鼠的脚被棉花怎么办 仓鼠的脚变黑了怎么办 夏天小仓鼠生了怎么办 把仓鼠摔出血了怎么办 仓鼠摔成骨折了怎么办 孩子被仓鼠咬了怎么办 仓鼠不咬磨牙石怎么办 仓鼠妈妈跑了宝宝怎么办 买的仓鼠繁殖了怎么办