如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的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技术剖析之十六:数据契约的等效性和版本控制


作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接

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

  1. WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

    [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NET中,所有的集合都实现了IEnumerable接口,比如Arra ...

  2. WCF技术剖析之十四:泛型数据契约和集合数据契约(上篇)

    在.NET Framework 2.0中,泛型第一次被引入.我们可以定义泛型接口.泛型类型.泛型委托和泛型方法.序列化依赖于真实具体的类型,而泛型则刻意模糊了具体类型概念.而集合代表一组对象的组合,集 ...

  3. WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

    在本篇文章中,我们将讨论WCF四大契约(服务契约.数据契约.消息契约和错误契约)之一的消息契约(Message Contract).服务契约关注于对服务操作的描述,数据契约关注于对于数据结构和格式的描 ...

  4. WCF技术剖析之三十二:一步步创建一个完整的分布式事务应用

    在完成了对于WCF事务编程(<上篇>.<中篇>.<下篇>)的介绍后,本篇文章将提供一个完整的分布式事务的WCF服务应用,通过本例,读者不仅仅会了解到如何编程实现事务 ...

  5. WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]

    在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术剖析(卷1)>的读者,应该会知道在第7章中我通过类似于AOP的 ...

  6. WCF技术剖析(卷1)正式出版

    [书     名] WCF技术剖析(卷1) [作     者] 蒋金楠 [出     版] 电子工业出版社 [书     号] 9787121089985 [出版日期] 2009 年7月 [开     ...

  7. 《WCF技术剖析(卷1)》(修订版)目录

    第1章  WCF简介 (WCF Overview) 1.1  SOA的基本概念和设计思想 1.2  WCF是对现有Windows平台下分布式通信技术的整合 1.3  构建一个简单的WCF应用 1.3. ...

  8. WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[WS标准篇]

    元数据实际上是服务终结点的描述,终结点由地址(Address).绑定(Binding)和契约(Contract)经典的ABC三要素组成.认真阅读过<WCF技术剖析(卷1)>的读者相对会对这 ...

  9. WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]

    WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过Ser ...

最新文章

  1. 链表和数组的区别在哪里
  2. 数据的表示方法和运算方法
  3. 合肥工贸高级技工学校计算机系,合肥工贸高级技工学校
  4. 几个大厂及 RCE漏洞二三事
  5. linux怎么看文件是否orc格式,hive文件存储格式orc,parquet,avro对比
  6. CAPL可以读写的几种文件
  7. DUXCMS 2.x学习问题(一)
  8. 计算机如何使用网络共享中心,电脑网络共享中心在哪
  9. Android 快速集成文档校正能力 超简单
  10. 中国国内可用API合集
  11. 干支纪年法简便算法_不用万年历快速推算日柱干支法
  12. freeSurfer颅骨剥离
  13. 基于线性回归预测环境空气质量 代码+数据
  14. Python PEP8 基本常用规范
  15. Uploader 上传插件示例用法和更新传递传输方法
  16. Python字符串对齐方式
  17. html2canvas的下载地址
  18. 远程调试neo4j-apoc组件
  19. zip,jar,tar文件解压
  20. OSPF 多区域实验报告

热门文章

  1. 【python】7-10 将输入的字符串中的字符头尾间隔输出(PTA)
  2. CUPS学习二:CUPS概念介绍。
  3. 2020-12-01 Kronecker积和范数的一些定理
  4. aws高可用mysql实现_Amazon RDS 的高可用性(多可用区) - Amazon Relational Database Service...
  5. 投资理财学习笔记二,1.4做个有钱人之富人思维
  6. zkSnark教程:从方程到验证
  7. HP laser 108W无线连接
  8. python爬取b站弹幕分析_B站弹幕爬取原理解析(python)
  9. Linux环境下搭建Apache服务器(完整版)
  10. CSV文件转换成shp文件后通过IDW法插值处理生成栅格