博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
阅读量:7209 次
发布时间:2019-06-29

本文共 8102 字,大约阅读时间需要 27 分钟。

如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的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
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
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 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
( 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
(contactToSerialize, fileName, dataContractSurrogate);
9: 
10: Contact contactToDeserialize = Deserialize
(fileName, dataContractSurrogate);
11: Console.WriteLine("contactToSerialize.Equals(contactToDeserialize) = {0}", contactToSerialize.Equals(contactToDeserialize) );

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

1: 
2:     
Bill
3:     
Gates
4:     
Male
5: 

输出结果:

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
()
8:                 as DataContractSerializerOperationBehavior;
9:             if (op.Behaviors.Find
()
10:  != null)
11:                 dataContractBehavior.DataContractSurrogate = new ContractSurrogate();
12:             op.Behaviors.Add(op.Behaviors.
13: Find
());
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技术剖析系列:


作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
八年磨一剑,阿里云ApsaraDB for HBase2.0正式上线
查看>>
HTTP/2 技术调研和性能分析
查看>>
ES6 javascript 实用开发技巧
查看>>
如何在新版的gitbook上写自己的书
查看>>
Java知识点总结(Java容器-ArrayList)
查看>>
Shared Event-loop for Same-Origin Windows(译)
查看>>
写一个简单易用可扩展vue表单验证插件(vue-validate-easy)
查看>>
使用stanford nlp进行依存句法分析
查看>>
JavaScript Promise查缺补漏
查看>>
你还不知“dubbo”是个什么东西吗???
查看>>
Gin实践 番外 Golang交叉编译
查看>>
【401天】跃迁之路——程序员高效学习方法论探索系列(实验阶段158-2018.03.13)...
查看>>
浅谈面试中常考的两种经典布局——圣杯与双飞翼
查看>>
「旁门右道」CURL持久连接技巧
查看>>
(十五) 构建springmvc+mybatis+dubbo分布式平台-window安装dubbo管控台
查看>>
Oracle - 安装 Oracle Database 11g Release 2
查看>>
JavaScript iterator 设计模式
查看>>
关于PHP的OpenSSL的加密问题
查看>>
iKcamp团队制作|基于Koa2搭建Node.js实战(含视频)☞ 中间件用法
查看>>
vue2 关于开发插件的几点思考
查看>>