wcf 基础教程 之已知类型KnownType 数据契约序列化DataContractSerializer

上一篇博客中我们介绍到了数据契约的序列化操作,虽然数据契约的序列化和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,这样就可以序列化成功。

我一直没搞清楚一个问题,就是我只是在抽象类中定义了已知类型,为什么接口的序列化也会成功呢?如果你知道的话,麻烦告知我一下,因为这个东西我一直没搞明白。

总结一下:已知类型是一种非常有用的定义或声明类型的方式,如果我们不能确定要序列化对象和声明类型之间的明确关系,那么我们就不妨添加已知类型来表示他的存在。但是在哪些场合会用到已知类型,我个人感觉已知类型的适用范围还是比较小的,就是解决未知类型的问题。

一件事如果你认为一次你做不好,那么就做两次,直到做好为止,这就叫进步。

 

posted @ 2013-03-29 18:52  baidixing  阅读(1305)  评论(2编辑  收藏  举报