现在的位置: 首页 > 综合 > 正文

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

2011年11月29日 ⁄ 综合 ⁄ 共 5563字 ⁄ 字号 评论关闭

[爱心链接:拯救一个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:

抱歉!评论已关闭.