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

[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的XML。反之,对于一段给定的基于数据契约的XML,要通过反序列化生成该类型的对象,我们该如何实现这样的场景?

比如下面定义了两个类型Contact和Customer,其中Customer是数据契约,Contact的Sex属性相当于Customer的Gender属性,而Contact的FullName可以看成是Customer的FirstName和LastName的组合。现在我们要做的是将一个Contact对象序列化成基于Customer数据契约对应的结构的XML,或者对于一段基于Customer数据契约对应结构的XML,将其反序列化生成Contact对象。

  1. 1: public class Contact

  1. 2: {

  1. 3: public string FullName

  1. 4: { get; set; }

  1. 5: 

  1. 6: public string Sex

  1. 7: { get; set; }

  1. 8: 

  1. 9: public override bool Equals(object obj)

  1. 10: {

  1. 11: Contact contact = obj as Contact;

  1. 12: if (contact == null)

  1. 13: {

  1. 14: return false;

  1. 15: }

  1. 16: 

  1. 17: return this.FullName == contact.FullName && this.Sex == contact.Sex;

  1. 18: }

  1. 19: 

  1. 20: public override int GetHashCode()

  1. 21: {

  1. 22: return this.FullName.GetHashCode() ^ this.Sex.GetHashCode();

  1. 23: }

  1. 24: }

  1. 1: [DataContract(Namespace = "http://www.artech.com")]

  1. 2: public class Customer

  1. 3: {

  1. 4: [DataMember(Order = 1)]

  1. 5: public string FirstName

  1. 6: { get; set; }

  1. 7: 

  1. 8: [DataMember(Order = 2)]

  1. 9: public string LastName

  1. 10: { get; set; }

  1. 11: 

  1. 12: [DataMember(Order = 3)]

  1. 13: public string Gender

  1. 14: { get; set; }

  1. 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. 1: public interface IDataContractSurrogate

  1. 2: {

  1. 3: Type GetDataContractType(Type type);

  1. 4: object GetObjectToSerialize(object obj, Type targetType);

  1. 5: object GetDeserializedObject(object obj, Type targetType);

  1. 6: 

  1. 7: object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType);

  1. 8: object GetCustomDataToExport(Type clrType, Type dataContractType);

  1. 9: void GetKnownCustomDataTypes(Collection<Type> customDataTypes);

  1. 10: Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData);

  1. 11: CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit);

  1. 12: }

现在我专门为Contact和Customer之间的转换创建了一个自定义的DataContractSurrogate:ContractSurrogate。在GetDataContractType中,如果发现类型是Contact,则替换成Customer。在GetObjectToSerialize方法中,将用于序列化的Contact对象用Customer对象替换,而在GetDeserializedObject中则用Contact对象替换反序列化生成的Customer对象。

  1. 1: public class ContractSurrogate : IDataContractSurrogate

  1. 2: {

  1. 3: 

  1. 4: public object GetCustomDataToExport(Type clrType, Type dataContractType)

  1. 5: {

  1. 6: return null;

  1. 7: }

  1. 8: 

  1. 9: public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)

  1. 10: {

  1. 11: return null;

  1. 12: }

  1. 13: 

  1. 14: public Type GetDataContractType(Type type)

  1. 15: {

  1. 16: if (type == typeof(Contact))

  1. 17: {

  1. 18: return typeof(Customer);

  1. 19: }

  1. 20: 

  1. 21: return type;

  1. 22: }

  1. 23: 

  1. 24: public object GetDeserializedObject(object obj, Type targetType)

  1. 25: {

  1. 26: Customer customer = obj as Customer;

  1. 27: if (customer == null)

  1. 28: {

  1. 29: return obj;

  1. 30: }

  1. 31: 

  1. 32: return new Contact

  1. 33: {

  1. 34: FullName = string.Format("{0} {1}", customer.FirstName, customer.LastName),

  1. 35: Sex = customer.Gender

  1. 36: };

  1. 37: }

  1. 38: 

  1. 39: public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)

  1. 40: {

  1. 41: 

  1. 42: }

  1. 43: 

  1. 44: public object GetObjectToSerialize(object obj, Type targetType)

  1. 45: {

  1. 46: Contact contact = obj as Contact;

  1. 47: if (contact == null)

  1. 48: {

  1. 49: return obj;

  1. 50: }

  1. 51: 

  1. 52: 

  1. 53: return new Customer

  1. 54: {

  1. 55: FirstName = contact.FullName.Split(" ".ToCharArray())[0],

  1. 56: LastName = contact.FullName.Split(" ".ToCharArray())[1],

  1. 57: Gender = contact.Sex

  1. 58: };

  1. 59: }

  1. 60: 

  1. 61: public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)

  1. 62: {

  1. 63: return null;

  1. 64: }

  1. 65: 

  1. 66: public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)

  1. 67: {

  1. 68: return typeDeclaration;

  1. 69: }

  1. 70: }

为了演示ContractSurrogate在序列化和反序列化中所起的作用,创建了Serialize<T>和Deserialize<T>两个辅助方法,通过创建DataContractSerializer进行序列化和反序列化。方法中的dataContractSurrogate参数被传入DataContractSerializer的构造函数中。

  1. 1: public static void Serialize<T>(T instance, string fileName, IDataContractSurrogate dataContractSurrogate)

  1. 2: {

  1. 3: DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, false, dataContractSurrogate);

  1. 4: using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))

  1. 5: {

  1. 6: serializer.WriteObject(writer, instance);

  1. 7: Process.Start(fileName);

  1. 8: }

  1. 9: }

  1. 10: 

  1. 11: public static T Deserialize<T>( string fileName, IDataContractSurrogate dataContractSurrogate)

  1. 12: {

  1. 13: DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, false, dataContractSurrogate);

  1. 14: using (XmlReader reader = new XmlTextReader(fileName))

  1. 15: {

  1. 16: return (T)serializer.ReadObject(reader);

  1. 17: }

  1. 18: }

借助于上面定义的ContractSurrogate和两个辅助方法,我们通过下面的程序演示IDataContractSurrogate在序列化和反序列化过程中所起的作用。

  1. 1: string fileName = @"E:\contact.xml";

  1. 2: Contact contactToSerialize = new Contact

  1. 3: {

  1. 4: FullName = "Bill Gates",

  1. 5: Sex = "Male"

  1. 6: };

  1. 7: IDataContractSurrogate dataContractSurrogate = new ContractSurrogate();

  1. 8: Serialize<Contact>(contactToSerialize, fileName, dataContractSurrogate);

  1. 9: 

  1. 10: Contact contactToDeserialize = Deserialize<Contact>(fileName, dataContractSurrogate);

  1. 11: Console.WriteLine("contactToSerialize.Equals(contactToDeserialize) = {0}", contactToSerialize.Equals(contactToDeserialize) );

下面的XML表述Contract对象被序列化后的结果,显而易见,这和真正序列化一个Customer对象是完全一样的。不仅如此,基于下面一段XML反序列化生成的Contact对象和用于序列化的对象是相等的,这通过最终的输出结果可以看出来。

  1. 1: <Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.artech.com">

  1. 2: <FirstName>Bill</FirstName>

  1. 3: <LastName>Gates</LastName>

  1. 4: <Gender>Male</Gender>

  1. 5: </Customer>

输出结果:

  1. 1: contactToSerialize.Equals(contactToDeserialize) = True

在进行服务寄宿的时候,可以通过如下代码指定IDataContractSurrogate。

  1. 1: using (ServiceHost serviceHost = new ServiceHost(typeof(InventoryCheck)))

  1. 2: foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints)

  1. 3: {

  1. 4: foreach (OperationDescription op in ep.Contract.Operations)

  1. 5: {

  1. 6: DataContractSerializerOperationBehavior dataContractBehavior =

  1. 7: op.Behaviors.Find<DataContractSerializerOperationBehavior>()

  1. 8: as DataContractSerializerOperationBehavior;

  1. 9: if (op.Behaviors.Find<DataContractSerializerOperationBehavior>()

  1. 10: != null)

  1. 11: dataContractBehavior.DataContractSurrogate = new ContractSurrogate();

  1. 12: op.Behaviors.Add(op.Behaviors.

  1. 13: Find<DataContractSerializerOperationBehavior>());

  1. 14: 

  1. 15: dataContractBehavior = new DataContractSerializerOperationBehavior(op);

  1. 16: dataContractBehavior.DataContractSurrogate =

  1. 17: new ContractSurrogate ();

  1. 18: op.Behaviors.Add(dataContractBehavior);

  1. 19: }

  1. 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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

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

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

    原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...

  2. WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)

    原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济 ...

  3. WCF技术剖析之十六:数据契约的等效性和版本控制

    原文:WCF技术剖析之十六:数据契约的等效性和版本控制 数据契约是对用于交换的数据结构的描述,是数据序列化和反序列化的依据.在一个WCF应用中,客户端和服务端必须通过等效的数据契约方能进行有效的数据交 ...

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

    原文:WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制 ...

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

    原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇] 在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道:当遇到某些异常,我们需要强行中止(Abor ...

  6. WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理

    原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境 ...

  7. WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)

    原文:WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话 ...

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

    原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术 ...

  9. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

随机推荐

  1. Qt之QNetworkInterface(查询网络接口),QHostInfo(查询主机IP)

    http://blog.csdn.net/u011012932/article/details/50775052 http://blog.csdn.net/u011012932/article/det ...

  2. IT第十一天、第十二天、第十三天 - 数组的应用、飞行棋游戏的编写和总结

    NIIT第十一天 上午 多维数组 1.数组是引用数据类型 排序 1.冒泡排序法 2.类冒泡排序法 下午 飞行棋游戏 1.项目策划 2.项目规则确认 3.项目模块确认 晚上 1.飞行棋游戏,项目框架的编 ...

  3. wiki oi 3115高精度练习之减法

    题目描述 Description 给出两个正整数A和B,计算A-B的值.保证A和B的位数不超过500位. 输入描述 Input Description 读入两个用空格隔开的正整数 输出描述 Outpu ...

  4. 解题报告 HDU1944 S-Nim

    S-Nim Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem De ...

  5. mysql语句:批量更新多条记录的不同值

    mysql更新语句很简单,更新一条数据的某个字段,一般这样写: 1 UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_va ...

  6. Js中的多条件排序,多列排序

    參见github: https://github.com/Teun/thenBy.js

  7. Android百度地图定位

    在谈到百度地图.如今,每个人都知道这个时候应该可以了吧.而更多的字不拉.直接朝话题. 访问百度地图api您必须应用key,应用在这里key不用说,有官方的文件说明如何应用上述key. 在这里,百度地图 ...

  8. android -- 蓝牙 bluetooth (二) 打开蓝牙

    4.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不同而已,后续的流程是一样的.先来看systemServe ...

  9. NYoj-街区最短路径问题

    街区最短路径问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描写叙述 一个街区有非常多住户,街区的街道仅仅能为东西.南北两种方向. 住户仅仅能够沿着街道行走. 各个街道之 ...

  10. ListActivity的注意点

    有的时候我们需要集成ListActivity,注意点1,这个时候我们的xml中的<ListView>标签中的id属性不能够随便自己命名,而是要固定为android:id="@id ...