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


    [爱心链接:拯救一个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技术剖析之十六:数据契约的等效性和版本控制


    作者:Artech
    出处:http://artech.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    常用正则表达式(数字、字符、固定格式)
    阿里云serverless使用笔记
    http的get请求与post请求区别
    HTTPS各版本安全策略HTTPS1_1_TLS1_0、HTTPS2_TLS1_0、HTTPS2_TLS1_2
    REST式的web服务
    JAVA Remote Object
    AJAX的核心XMLHttpRequest对象
    SQL中TOP,LIMIT,ROWNUM的用法
    半双工模式和全双工模式
    win7系统网卡驱动正常,网线连接设备正常,但电脑右下角网络图片显示一直在转圈或者显示一个黄色感叹号的解决办法
  • 原文地址:https://www.cnblogs.com/artech/p/1527283.html
Copyright © 2020-2023  润新知