本来今天打算描述一下数据契约的序列化,毕竟只是单纯的说数据契约的作用也没有太大意义,但是我发现如果单纯的叙述wcf的序列胡DataSerializer 很困难,因为它采用的事xml序列化,所以今天打乱了我的计划,来介绍一下.Net中的xml序列化,毕竟我们在使用序列化器的时候,很多时候生成的都是xml。
契约是交互双方或多方就某个问题达成的一共共识,而信息交换式wcf通信的唯一手段,也是跨平台的关键,所以契约的最根本目的不是定义什么操作方式,而是对消息的结构进行规范、统一,只有通信双方对消息的结构达成了一致,通信才可能进行。
wcf默认的数据交换方式就是xml,虽然说数据在wcf中有CLR对象和Xml两种,但是CLR对象时强类型语言才会有的,而xml才是可以和其他系统通信的手段。CLR对象要传输到远程客户端,需要序列化,默认的使用xml传输,那么就会使用到Xml序列化,今天我们一起来了解一下,.Net中如何把数据序列化成xml的,或者说其中有什么规律存在,因为打乱了我的计划,所以说这篇博客可能会显得有一点乱,没有条理,因为我是想到哪儿,写到哪儿,各位莫怪。
其中有一点,wcf虽然默认的事采用xml序列化,但是不是说不可以采用其他格式序列化,json也可以正常传输,并且可能是我们的wcf服务和手机客户端通信的首选。
在.Net中,.Net framework 就对基于XMl的序列化提供了原生的支持,比如传统的web服务就是通过XmlSerializer进行序列化和反序列化,xmlSerializer也可以应用到wcf中,因为wcf也是作为.Net framework的一个组件提供的。
我们来看一下默认的xml生成规则。这个东西说不是特别好说,还是通过一个简单的示例让我们一起来了解它,有码有真相。项目还是采用我们原来的项目,只是我们这次在控制台运行。
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 9 namespace Chinaer.WcfDemo.ConsoleClient10 {11 class Program12 {13 static void Main(string[] args)14 {15 Person person = new Person(25, Guid.NewGuid())16 {17 Date = DateTime.Now18 };19 Serialize(person, "person.xml");20 Console.Read();21 }22 23 /// 24 /// 序列化方法25 /// 26 ///27 /// 28 /// 29 public static void Serialize (T instace, string fileName)30 {31 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))32 {33 XmlSerializer serializer = new XmlSerializer(typeof(T));34 serializer.Serialize(writer, instace);35 }36 Process.Start(fileName);37 }38 }39 /// 40 /// 定义一个实体类 Person41 /// 42 public class Person43 {44 //注意我们没有默认的构造函数45 private double Age { get; set; } //私有字段 年龄46 47 public Guid ID { get; set; } //公有的随机数48 49 public DateTime Date { get; set; }50 51 public Person(double age, Guid id)52 {53 this.Age = age;54 this.ID = id;55 }56 }57 58 59 60 61 62 63 }
请注意,如果你直接运行上面的代码会出现异常信息。
为什么我没有添加默认的空的无参数的构造函数,就会出现异常呢?因为在反序列化的时候需要调用它。在反序列化的时候会调用无参数的空构造函数来生成目标对象,然后填充数值。
下面我们添加空的无参数的构造函数。
1 23 1d988f1f-3fe4-463b-84ce-7f771449280d 42013-03-21T22:19:34.7687866+08:00 5
通过生成的xml文件,对应我们定义在类Person中的属性我们可以得到如下几点有用的信息:
- xml根节点的名称为对象类型的名称,或者说是类名
- 对象属性或字段以xmlElement xml元素的形式输出,名称和他们的名称一致
- 只有public类型的成员才会被序列化,我们在xml文件中没有看到Age,因为它是private 标识的,其他如internal的也不会被序列化
- xml元素出现的顺序和他们在类中存在的顺序是一致的。
下面我们再来思考一个问题,如果属性是只读的或者是只写的,那会出现什么情况呢?程序员的性格就是一切以程序运行为准,那么我们就来测试一下结果吧。请再次注意,这次我做的修改,我更改属性为只读或者只写,并且我还添加了两个属性,但是我在实例化对象的时候,并没有为它赋值,或者我们可以为UserName赋值。
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 9 namespace Chinaer.WcfDemo.ConsoleClient10 {11 class Program12 {13 static void Main(string[] args)14 {15 Person person = new Person(25, Guid.NewGuid())16 {17 Date = DateTime.Now18 };19 20 person.UserName = "guozhiqi";21 //person.UserPwd = "123";22 Serialize(person, "person.xml");23 Console.Read();24 }25 26 /// 27 /// 序列化方法28 /// 29 ///30 /// 31 /// 32 public static void Serialize (T instace, string fileName)33 {34 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))35 {36 XmlSerializer serializer = new XmlSerializer(typeof(T));37 serializer.Serialize(writer, instace);38 }39 Process.Start(fileName);40 }41 }42 /// 43 /// 定义一个实体类 Person44 /// 45 public class Person46 {47 private Guid _id;48 49 private DateTime _date;50 //注意我们没有默认的构造函数51 internal double Age { get; set; } //私有字段 年龄52 53 public Guid ID { get { return _id; } } //公有的随机数54 55 public DateTime Date { set { _date = value; } }56 57 public string UserName { get; set; }58 59 public string UserPwd { get; set; }60 public Person() { }61 public Person(double age, Guid id)62 {63 this.Age = age;64 65 }66 }67 68 69 70 71 72 73 }
请再次注意我做的更改,添加了两个属性UserName和UserPwd,并且只为UserName赋值,而没有为UserPwd赋值,还有我更改ID为只读,date为只写属性,下面我们来看生成的xml。
guozhiqi
请再次对比我们生成的xml文件和类之间的关系,由此我们再次总结几点:
- 只读或只写的属性不会被序列化,只有可读写的属性可以序列化。当然字段不存在是否只读或只写,所以是可序列化的,它只受到public等标示符的影响。
- 如果在实例化对象时,不会对象的属性赋值,那么它是不会序列化的。这就告诉我们,如果再传递参数的时候,如果想传递一个空,千万不要认为不为对象赋值就可以,我们必须制定一个值,它才会序列化。
既然说到了访问修饰符、只读或只写,但是我印象中只读或只写我们可以定义,举例来说,如果我们定义只读属性,那么我们设置set为private也应该可以实现只读功能。那么我们就来尝试一下这个是否可以序列化。
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 9 namespace Chinaer.WcfDemo.ConsoleClient10 {11 class Program12 {13 static void Main(string[] args)14 {15 Person person = new Person(25, Guid.NewGuid())16 {17 Date = DateTime.Now18 };19 20 person.UserName = "guozhiqi";21 //person.UserPwd = "123";22 Serialize(person, "person.xml");23 Console.Read();24 }25 26 /// 27 /// 序列化方法28 /// 29 ///30 /// 31 /// 32 public static void Serialize (T instace, string fileName)33 {34 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))35 {36 XmlSerializer serializer = new XmlSerializer(typeof(T));37 serializer.Serialize(writer, instace);38 }39 Process.Start(fileName);40 }41 }42 /// 43 /// 定义一个实体类 Person44 /// 45 public class Person46 {47 private Guid _id;48 49 private DateTime _date;50 //注意我们没有默认的构造函数51 internal double Age { get; set; } //私有字段 年龄52 53 public Guid ID { get; private set; } //公有的随机数54 55 public DateTime Date { set; private get; }56 57 public string UserName { get; set; }58 59 public string UserPwd { get; set; }60 public Person() { }61 public Person(double age, Guid id)62 {63 this.Age = age;64 65 }66 }67 68 }
这一次做的修改很小,只是简单的把自动属性的set设置为了private set或者get设置为了private get;这也是实现了只读或只写功能。那么我们运行程序是否可以得到我们想要的结果呢?
不可思议的一幕出现了,只写属性get不写不会被序列化,但是如果加上private get 就会出现异常?这是什么原因呢?我们尝试解决办法,毕竟这个异常信息提供的错误信息太少。
打开异常详细信息,我们看到了一个有用的信息,未将对象引用到对象的示例,可以说这是我调试程序过程中见得最多,但是最难解决的难题。
既然有了目标,那肯定是更改的属性出了问题,我们使用的是自动属性,如果我们更改为普通的字段是否可以呢?试试吧
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 9 namespace Chinaer.WcfDemo.ConsoleClient10 {11 class Program12 {13 static void Main(string[] args)14 {15 Person person = new Person(25, Guid.NewGuid())16 {17 Date = DateTime.Now18 };19 20 person.UserName = "guozhiqi";21 //person.UserPwd = "123";22 Serialize(person, "person.xml");23 Console.Read();24 }25 26 /// 27 /// 序列化方法28 /// 29 ///30 /// 31 /// 32 public static void Serialize (T instace, string fileName)33 {34 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))35 {36 XmlSerializer serializer = new XmlSerializer(typeof(T));37 serializer.Serialize(writer, instace);38 }39 Process.Start(fileName);40 }41 }42 /// 43 /// 定义一个实体类 Person44 /// 45 public class Person46 {47 private Guid _id;48 49 private DateTime _date;50 //注意我们没有默认的构造函数51 internal double Age { get; set; } //私有字段 年龄52 53 public Guid ID54 {55 get { return _id; }56 private set57 {58 _id = value;59 }60 } //公有的随机数61 62 public DateTime Date63 {64 private set65 {66 _date = value;67 }68 get69 {70 return _date;71 }72 }73 74 public string UserName { get; set; }75 76 public string UserPwd { get; set; }77 public Person() { }78 public Person(double age, Guid id)79 {80 this.Age = age;81 82 }83 }84 85 }
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 9 namespace Chinaer.WcfDemo.ConsoleClient10 {11 class Program12 {13 static void Main(string[] args)14 {15 Person person = new Person(25, Guid.NewGuid())16 {17 Date = DateTime.Now18 };19 20 person.UserName = "guozhiqi";21 //person.UserPwd = "123";22 Serialize(person, "person.xml");23 Console.Read();24 }25 26 /// 27 /// 序列化方法28 /// 29 ///30 /// 31 /// 32 public static void Serialize (T instace, string fileName)33 {34 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))35 {36 XmlSerializer serializer = new XmlSerializer(typeof(T));37 serializer.Serialize(writer, instace);38 }39 Process.Start(fileName);40 }41 }42 /// 43 /// 定义一个实体类 Person44 /// 45 public class Person46 {47 private Guid _id;48 49 private DateTime _date;50 //注意我们没有默认的构造函数51 internal double Age { get; set; } //私有字段 年龄52 53 public Guid ID54 {55 get { return _id; }56 private set57 {58 _id = value;59 }60 } //公有的随机数61 62 public DateTime Date63 {64 private set65 {66 _date = value;67 }68 get69 {70 return _date;71 }72 }73 74 public string UserName { get; set; }75 76 public string UserPwd { get; set; }77 public Person() { }78 public Person(double age, Guid id)79 {80 this.Age = age;81 82 }83 }84 85 }
请注意我这次的修改,只是添加了私有字段,让属性使用,但是我没有更改任何地方,但是运行起来的结果仍然让我很无奈,直呼程序员 真是伤不起啊。
遇到了和刚才一样的异常信息,无奈啊,现在我只是看到那个应用在属性上的private最可疑,我们试着去掉它,那么就和我们上面做的只读或只写的一样的,肯定是正确的,我也测试了,确实正确,不再赘述了。
好了,这次就写这么多,我再写下一篇,控制xml的序列化,因为博客园每隔三个小时才可以发布到博客园首页,所以我先提供一下,记得有时间浏览一下奥。
总结一下,xml的序列化没有什么重点,这篇博客重点就是发现了.Net序列化成xml的过程中我们可能遇到的几个原则。虽然知识点不大,但是对我们以后理解wcf的序列化器也是很有帮助的。
下一篇