上一篇博客中我们介绍到了数据契约的序列化操作,虽然数据契约的序列化和xml的序列化操作基本保持一致,除了一些细小的差别外,但是数据契约的序列化更加简单,更加方便。今天我们继续数据契约的序列化,只不过今天要讨论的问题不是如何序列化,而是如何保证序列化成功。
说到这里,你可能会笑了?保证序列化成功是wcf的操作,我们根本不用管,如果序列化不成功,也是wcf框架的问题,肯定不会是我序列化对象的问题。
其实对象的序列化和反序列化实现了数据在托管对象和xml之间两种形态的转换。由于托管对象是通过CLR类型来描述的,所以数据契约序列化器DatacontractSerializer在序列化的时候必须明确所有对象的真实类型。这里有一个概念叫做真实类型。究竟什么是真实类型呢?
.Net的类型可以分为声明类型和真实类型两种。在编程的时候我们只需要声明类型的声明类型即可,比如接口或抽象类。举个简单的例子来说明一下这个问题的存在。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Xml; 6 using System.Xml.Serialization; 7 using System.Diagnostics; 8 using System.Runtime.Serialization; 9 10 namespace Chinaer.WcfDemo.ConsoleClient 11 { 12 13 public interface IOrder 14 { 15 string UserName { get; set; } 16 string UserPwd { get; set; } 17 } 18 [DataContract] 19 public abstract class OrderBase : IOrder 20 { 21 [DataMember] 22 public string UserName 23 { 24 get; 25 set; 26 } 27 [DataMember] 28 public string UserPwd 29 { 30 get; 31 set; 32 } 33 } 34 35 [DataContract] 36 public class Order : OrderBase 37 { 38 [DataMember] 39 public string UserAge { get; set; } 40 } 41 public class Exec 42 { 43 public void Execute() 44 { 45 List<Order> list = new List<Order>(); 46 for (int i = 0; i < 10; i++) 47 { 48 Order order = new Order() { UserName = "userName" }; 49 list.Add(order); 50 } 51 } 52 } 53 54 55 class Program 56 { 57 static void Main(string[] args) 58 { 59 Order order = new Order() 60 { 61 UserAge = "24", 62 UserName = "guozhiqi", 63 UserPwd = "love" 64 }; 65 DataContractSerialize<IOrder>(order, "interfact.xml",false); 66 DataContractSerialize<OrderBase>(order, "abstract.xml",false); 67 68 69 Console.Read(); 70 } 71 /// <summary> 72 /// DataContractSerializer序列化 73 /// </summary> 74 /// <typeparam name="T"></typeparam> 75 /// <param name="instance"></param> 76 /// <param name="fileName"></param> 77 public static void DataContractSerialize<T>(T instance, string fileName, bool preserveReference) 78 { 79 DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, 50, false, preserveReference, null); 80 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 81 { 82 serializer.WriteObject(writer, instance); 83 84 } 85 Process.Start(fileName); 86 } 87 /// <summary> 88 /// 序列化方法 89 /// </summary> 90 /// <typeparam name="T"></typeparam> 91 /// <param name="instace"></param> 92 /// <param name="fileName"></param> 93 public static void Serialize<T>(T instace, string fileName) 94 { 95 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 96 { 97 XmlSerializer serializer = new XmlSerializer(typeof(T)); 98 serializer.Serialize(writer, instace); 99 } 100 Process.Start(fileName); 101 } 102 } 103 [XmlRoot(ElementName = "guozhiqi", Namespace = "http://www.guozhiqi.com")] 104 /// <summary> 105 /// 定义一个实体类 Person 106 /// </summary> 107 public class Person 108 { 109 private Guid _id; 110 111 private DateTime _date; 112 //注意我们没有默认的构造函数 113 internal double Age { get; set; } //私有字段 年龄 114 /// <summary> 115 /// 通过XmlAttributeAttribute 序列化成xml属性 116 /// </summary> 117 [XmlAttribute(AttributeName = "GuidID", Namespace = "http://guidID")] 118 public Guid ID 119 { 120 get { return _id; } 121 set 122 { 123 _id = value; 124 } 125 } //公有的随机数 126 [XmlElement(ElementName = "DateTime", Namespace = "http://date")] 127 public DateTime Date 128 { 129 set 130 { 131 _date = value; 132 } 133 get 134 { 135 return _date; 136 } 137 } 138 139 public string UserName { get; set; } 140 141 public string UserPwd { get; set; } 142 public Person() { } 143 public Person(double age, Guid id) 144 { 145 this.Age = age; 146 147 } 148 } 149 150 }
我来简单的说明一下上面的代码,我们定义了一个接口IOrder和一个抽象类OrderBase继承自IOrder,并且我们定义了一个类Order继承自OrderBase。
我们实例化了一个order对象,但是我们在序列化的时候声明类型一个为IOrder,另一个为OrderBase抽象类。但是序列化的对象为Order对象。这是否可以说明我们的问题呢。我们来运行一下,看下结果。
出现了这么一个错误的信息,虽然说wcf的异常信息不能使我们找到问题所在,但是这个还可以,最起码告诉了我们应该如何做,它告诉我们存在未知类型,无法序列化。这就是我们今天要描述的主角:已知类型Known Type
首先我们现在要做的问题就是要解决掉这个异常信息的存在,让我们修改一下代码,添加已知类型的声明。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Xml; 6 using System.Xml.Serialization; 7 using System.Diagnostics; 8 using System.Runtime.Serialization; 9 10 namespace Chinaer.WcfDemo.ConsoleClient 11 { 12 13 public interface IOrder 14 { 15 string UserName { get; set; } 16 string UserPwd { get; set; } 17 } 18 [DataContract] 19 public abstract class OrderBase : IOrder 20 { 21 [DataMember] 22 public string UserName 23 { 24 get; 25 set; 26 } 27 [DataMember] 28 public string UserPwd 29 { 30 get; 31 set; 32 } 33 } 34 35 [DataContract] 36 public class Order : OrderBase 37 { 38 [DataMember] 39 public string UserAge { get; set; } 40 } 41 public class Exec 42 { 43 public void Execute() 44 { 45 List<Order> list = new List<Order>(); 46 for (int i = 0; i < 10; i++) 47 { 48 Order order = new Order() { UserName = "userName" }; 49 list.Add(order); 50 } 51 } 52 } 53 54 55 class Program 56 { 57 static void Main(string[] args) 58 { 59 Order order = new Order() 60 { 61 UserAge = "24", 62 UserName = "guozhiqi", 63 UserPwd = "love" 64 }; 65 DataContractSerialize<IOrder>(order, "interfact.xml",false,typeof(Order)); 66 DataContractSerialize<OrderBase>(order, "abstract.xml",false,typeof(Order)); 67 68 69 Console.Read(); 70 } 71 /// <summary> 72 /// DataContractSerializer序列化 73 /// </summary> 74 /// <typeparam name="T"></typeparam> 75 /// <param name="instance"></param> 76 /// <param name="fileName"></param> 77 public static void DataContractSerialize<T>(T instance, string fileName, bool preserveReference,params Type[]knownTypes) 78 { 79 DataContractSerializer serializer = new DataContractSerializer(typeof(T),knownTypes, 50, false, preserveReference, null); 80 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 81 { 82 serializer.WriteObject(writer, instance); 83 84 } 85 Process.Start(fileName); 86 } 87 /// <summary> 88 /// 序列化方法 89 /// </summary> 90 /// <typeparam name="T"></typeparam> 91 /// <param name="instace"></param> 92 /// <param name="fileName"></param> 93 public static void Serialize<T>(T instace, string fileName) 94 { 95 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 96 { 97 XmlSerializer serializer = new XmlSerializer(typeof(T)); 98 serializer.Serialize(writer, instace); 99 } 100 Process.Start(fileName); 101 } 102 } 103 [XmlRoot(ElementName = "guozhiqi", Namespace = "http://www.guozhiqi.com")] 104 /// <summary> 105 /// 定义一个实体类 Person 106 /// </summary> 107 public class Person 108 { 109 private Guid _id; 110 111 private DateTime _date; 112 //注意我们没有默认的构造函数 113 internal double Age { get; set; } //私有字段 年龄 114 /// <summary> 115 /// 通过XmlAttributeAttribute 序列化成xml属性 116 /// </summary> 117 [XmlAttribute(AttributeName = "GuidID", Namespace = "http://guidID")] 118 public Guid ID 119 { 120 get { return _id; } 121 set 122 { 123 _id = value; 124 } 125 } //公有的随机数 126 [XmlElement(ElementName = "DateTime", Namespace = "http://date")] 127 public DateTime Date 128 { 129 set 130 { 131 _date = value; 132 } 133 get 134 { 135 return _date; 136 } 137 } 138 139 public string UserName { get; set; } 140 141 public string UserPwd { get; set; } 142 public Person() { } 143 public Person(double age, Guid id) 144 { 145 this.Age = age; 146 147 } 148 } 149 150 }
聪明的你看到了我们如何修改代码,避免刚才异常信息的存在了吗?其实我们就是在数据契约的序列化器中添加了已知类型,才避免了这个错误的发生。好,下面让我们来看一下基于接口的序列化生成的xml是什么样的呢?
1 <z:anyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 2 xmlns:d1p1="http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.ConsoleClient" 3 i:type="d1p1:Order" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> 4 <d1p1:UserName>guozhiqi</d1p1:UserName> 5 <d1p1:UserPwd>love</d1p1:UserPwd> 6 <d1p1:UserAge>24</d1p1:UserAge> 7 </z:anyType>
其实和xml序列化生成的内容基本相同,但是请注意xml的根节点,这个基于接口的xml的根节点是anyType,而不是接口名。这是为什么呢?其实我们知道接口只是定义,而不能有具体的对象,但是我们序列化的时候序列化的对象一般都是继承自该接口,所以仅仅根据接口并不能明确的知道我们序列化的对象的真实类型。所以生成的xml才会采用anyType,其实在.Net中对象的就是System.Object,表示可以表示任何对象。
当然了我们不能总是自己定义序列化器,而wcf提供的序列化器才是我们关注的关键,那么究竟应该如何根据wcf的规则定义已知类型呢?
其实声明已知类型的方式有多种,我们就介绍最简单的哪种,至于配置文件的方式还有一个根据静态方法返回已知类型列表的方式我们今天不再赘述。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Xml; 6 using System.Xml.Serialization; 7 using System.Diagnostics; 8 using System.Runtime.Serialization; 9 10 namespace Chinaer.WcfDemo.ConsoleClient 11 { 12 13 14 public interface IOrder 15 { 16 string UserName { get; set; } 17 string UserPwd { get; set; } 18 } 19 [DataContract] 20 [KnownType(typeof(Order))] 21 public abstract class OrderBase : IOrder 22 { 23 [DataMember] 24 public string UserName 25 { 26 get; 27 set; 28 } 29 [DataMember] 30 public string UserPwd 31 { 32 get; 33 set; 34 } 35 } 36 37 [DataContract] 38 public class Order : OrderBase 39 { 40 [DataMember] 41 public string UserAge { get; set; } 42 } 43 public class Exec 44 { 45 public void Execute() 46 { 47 List<Order> list = new List<Order>(); 48 for (int i = 0; i < 10; i++) 49 { 50 Order order = new Order() { UserName = "userName" }; 51 list.Add(order); 52 } 53 } 54 } 55 56 57 class Program 58 { 59 static void Main(string[] args) 60 { 61 Order order = new Order() 62 { 63 UserAge = "24", 64 UserName = "guozhiqi", 65 UserPwd = "love" 66 }; 67 DataContractSerialize<IOrder>(order, "interfact.xml", false); 68 DataContractSerialize<OrderBase>(order, "abstract.xml", false); 69 70 71 Console.Read(); 72 } 73 /// <summary> 74 /// DataContractSerializer序列化 75 /// </summary> 76 /// <typeparam name="T"></typeparam> 77 /// <param name="instance"></param> 78 /// <param name="fileName"></param> 79 public static void DataContractSerialize<T>(T instance, string fileName, bool preserveReference) 80 { 81 DataContractSerializer serializer = new DataContractSerializer(typeof(T), null, 50, false, preserveReference, null); 82 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 83 { 84 serializer.WriteObject(writer, instance); 85 86 } 87 Process.Start(fileName); 88 } 89 /// <summary> 90 /// 序列化方法 91 /// </summary> 92 /// <typeparam name="T"></typeparam> 93 /// <param name="instace"></param> 94 /// <param name="fileName"></param> 95 public static void Serialize<T>(T instace, string fileName) 96 { 97 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) 98 { 99 XmlSerializer serializer = new XmlSerializer(typeof(T)); 100 serializer.Serialize(writer, instace); 101 } 102 Process.Start(fileName); 103 } 104 } 105 [XmlRoot(ElementName = "guozhiqi", Namespace = "http://www.guozhiqi.com")] 106 /// <summary> 107 /// 定义一个实体类 Person 108 /// </summary> 109 public class Person 110 { 111 private Guid _id; 112 113 private DateTime _date; 114 //注意我们没有默认的构造函数 115 internal double Age { get; set; } //私有字段 年龄 116 /// <summary> 117 /// 通过XmlAttributeAttribute 序列化成xml属性 118 /// </summary> 119 [XmlAttribute(AttributeName = "GuidID", Namespace = "http://guidID")] 120 public Guid ID 121 { 122 get { return _id; } 123 set 124 { 125 _id = value; 126 } 127 } //公有的随机数 128 [XmlElement(ElementName = "DateTime", Namespace = "http://date")] 129 public DateTime Date 130 { 131 set 132 { 133 _date = value; 134 } 135 get 136 { 137 return _date; 138 } 139 } 140 141 public string UserName { get; set; } 142 143 public string UserPwd { get; set; } 144 public Person() { } 145 public Person(double age, Guid id) 146 { 147 this.Age = age; 148 149 } 150 } 151 152 }
我们还是把代码还原到我们刚才出现异常的时候,但是这次我们在抽象类OrderBase中添加了已知类型KnownType 指定的类型是Order,这样就可以序列化成功。
我一直没搞清楚一个问题,就是我只是在抽象类中定义了已知类型,为什么接口的序列化也会成功呢?如果你知道的话,麻烦告知我一下,因为这个东西我一直没搞明白。
总结一下:已知类型是一种非常有用的定义或声明类型的方式,如果我们不能确定要序列化对象和声明类型之间的明确关系,那么我们就不妨添加已知类型来表示他的存在。但是在哪些场合会用到已知类型,我个人感觉已知类型的适用范围还是比较小的,就是解决未知类型的问题。
一件事如果你认为一次你做不好,那么就做两次,直到做好为止,这就叫进步。
我又回来了,回到了技术最前线,