[爱心链接:拯救一个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<!--CRLF-->
2: {
<!--CRLF-->
3: public string FullName<!--CRLF-->
4: { get; set; }
<!--CRLF-->
5:
<!--CRLF-->
6: public string Sex<!--CRLF-->
7: { get; set; }
<!--CRLF-->
8:
<!--CRLF-->
9: public override bool Equals(object obj)<!--CRLF-->
10: {
<!--CRLF-->
11: Contact contact = obj as Contact;<!--CRLF-->
12: if (contact == null)<!--CRLF-->
13: {
<!--CRLF-->
14: return false;<!--CRLF-->
15: }
<!--CRLF-->
16:
<!--CRLF-->
17: return this.FullName == contact.FullName && this.Sex == contact.Sex;<!--CRLF-->
18: }
<!--CRLF-->
19:
<!--CRLF-->
20: public override int GetHashCode()<!--CRLF-->
21: {
<!--CRLF-->
22: return this.FullName.GetHashCode() ^ this.Sex.GetHashCode();<!--CRLF-->
23: }
<!--CRLF-->
24: }
<!--CRLF-->
1: [DataContract(Namespace = "http://www.artech.com")]<!--CRLF-->
2: public class Customer<!--CRLF-->
3: {
<!--CRLF-->
4: [DataMember(Order = 1)]
<!--CRLF-->
5: public string FirstName<!--CRLF-->
6: { get; set; }
<!--CRLF-->
7:
<!--CRLF-->
8: [DataMember(Order = 2)]
<!--CRLF-->
9: public string LastName<!--CRLF-->
10: { get; set; }
<!--CRLF-->
11:
<!--CRLF-->
12: [DataMember(Order = 3)]
<!--CRLF-->
13: public string Gender<!--CRLF-->
14: { get; set; }
<!--CRLF-->
15: }
<!--CRLF-->
为实现上面的要求,要涉及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<!--CRLF-->
2: {
<!--CRLF-->
3: Type GetDataContractType(Type type);
<!--CRLF-->
4: object GetObjectToSerialize(object obj, Type targetType);<!--CRLF-->
5: object GetDeserializedObject(object obj, Type targetType);<!--CRLF-->
6:
<!--CRLF-->
7: object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType);<!--CRLF-->
8: object GetCustomDataToExport(Type clrType, Type dataContractType);<!--CRLF-->
9: void GetKnownCustomDataTypes(Collection<Type> customDataTypes);<!--CRLF-->
10: Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData);<!--CRLF-->
11: CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit);
<!--CRLF-->
12: }
<!--CRLF-->
现在我专门为Contact和Customer之间的转换创建了一个自定义的DataContractSurrogate:ContractSurrogate。在GetDataContractType中,如果发现类型是Contact,则替换成Customer。在GetObjectToSerialize方法中,将用于序列化的Contact对象用Customer对象替换,而在GetDeserializedObject中则用Contact对象替换反序列化生成的Customer对象。
1: public class ContractSurrogate : IDataContractSurrogate<!--CRLF-->
2: {
<!--CRLF-->
3:
<!--CRLF-->
4: public object GetCustomDataToExport(Type clrType, Type dataContractType)<!--CRLF-->
5: {
<!--CRLF-->
6: return null;<!--CRLF-->
7: }
<!--CRLF-->
8:
<!--CRLF-->
9: public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)<!--CRLF-->
10: {
<!--CRLF-->
11: return null;<!--CRLF-->
12: }
<!--CRLF-->
13:
<!--CRLF-->
14: public Type GetDataContractType(Type type)<!--CRLF-->
15: {
<!--CRLF-->
16: if (type == typeof(Contact))<!--CRLF-->
17: {
<!--CRLF-->
18: return typeof(Customer);<!--CRLF-->
19: }
<!--CRLF-->
20:
<!--CRLF-->
21: return type;<!--CRLF-->
22: }
<!--CRLF-->
23:
<!--CRLF-->
24: public object GetDeserializedObject(object obj, Type targetType)<!--CRLF-->
25: {
<!--CRLF-->
26: Customer customer = obj as Customer;<!--CRLF-->
27: if (customer == null)<!--CRLF-->
28: {
<!--CRLF-->
29: return obj;<!--CRLF-->
30: }
<!--CRLF-->
31:
<!--CRLF-->
32: return new Contact<!--CRLF-->
33: {
<!--CRLF-->
34: FullName = string.Format("{0} {1}", customer.FirstName, customer.LastName),<!--CRLF-->
35: Sex = customer.Gender
<!--CRLF-->
36: };
<!--CRLF-->
37: }
<!--CRLF-->
38:
<!--CRLF-->
39: public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)<!--CRLF-->
40: {
<!--CRLF-->
41:
<!--CRLF-->
42: }
<!--CRLF-->
43:
<!--CRLF-->
44: public object GetObjectToSerialize(object obj, Type targetType)<!--CRLF-->
45: {
<!--CRLF-->
46: Contact contact = obj as Contact;<!--CRLF-->
47: if (contact == null)<!--CRLF-->
48: {
<!--CRLF-->
49: return obj;<!--CRLF-->
50: }
<!--CRLF-->
51:
<!--CRLF-->
52:
<!--CRLF-->
53: return new Customer<!--CRLF-->
54: {
<!--CRLF-->
55: FirstName = contact.FullName.Split(" ".ToCharArray())[0],<!--CRLF-->
56: LastName = contact.FullName.Split(" ".ToCharArray())[1],<!--CRLF-->
57: Gender = contact.Sex
<!--CRLF-->
58: };
<!--CRLF-->
59: }
<!--CRLF-->
60:
<!--CRLF-->
61: public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)<!--CRLF-->