wcf 基础教程 契约 Contract 数据契约DataContract序列化前身 XmlSerializer xml序列化

本来今天打算描述一下数据契约的序列化,毕竟只是单纯的说数据契约的作用也没有太大意义,但是我发现如果单纯的叙述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.ConsoleClient
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person person = new Person(25, Guid.NewGuid())
16             {
17                 Date = DateTime.Now
18             };
19             Serialize<Person>(person, "person.xml");
20             Console.Read();
21         }
22 
23         /// <summary>
24         /// 序列化方法
25         /// </summary>
26         /// <typeparam name="T"></typeparam>
27         /// <param name="instace"></param>
28         /// <param name="fileName"></param>
29         public static void Serialize<T>(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     /// <summary>
40     /// 定义一个实体类 Person
41     /// </summary>
42     public class Person
43     {
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 <?xml version="1.0" encoding="utf-8"?>
2 <Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3 <ID>1d988f1f-3fe4-463b-84ce-7f771449280d</ID>
4 <Date>2013-03-21T22:19:34.7687866+08:00</Date>
5 </Person>

通过生成的xml文件,对应我们定义在类Person中的属性我们可以得到如下几点有用的信息:

  1. xml根节点的名称为对象类型的名称,或者说是类名
  2. 对象属性或字段以xmlElement xml元素的形式输出,名称和他们的名称一致
  3. 只有public类型的成员才会被序列化,我们在xml文件中没有看到Age,因为它是private 标识的,其他如internal的也不会被序列化
  4. 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.ConsoleClient
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person person = new Person(25, Guid.NewGuid())
16             {
17                 Date = DateTime.Now
18             };
19 
20             person.UserName = "guozhiqi";
21             //person.UserPwd = "123";
22             Serialize<Person>(person, "person.xml");
23             Console.Read();
24         }
25 
26         /// <summary>
27         /// 序列化方法
28         /// </summary>
29         /// <typeparam name="T"></typeparam>
30         /// <param name="instace"></param>
31         /// <param name="fileName"></param>
32         public static void Serialize<T>(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     /// <summary>
43     /// 定义一个实体类 Person
44     /// </summary>
45     public class Person
46     {
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。

<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <UserName>guozhiqi</UserName>
</Person>

请再次对比我们生成的xml文件和类之间的关系,由此我们再次总结几点:

  1. 只读或只写的属性不会被序列化,只有可读写的属性可以序列化。当然字段不存在是否只读或只写,所以是可序列化的,它只受到public等标示符的影响。
  2. 如果在实例化对象时,不会对象的属性赋值,那么它是不会序列化的。这就告诉我们,如果再传递参数的时候,如果想传递一个空,千万不要认为不为对象赋值就可以,我们必须制定一个值,它才会序列化。

既然说到了访问修饰符、只读或只写,但是我印象中只读或只写我们可以定义,举例来说,如果我们定义只读属性,那么我们设置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.ConsoleClient
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person person = new Person(25, Guid.NewGuid())
16             {
17                 Date = DateTime.Now
18             };
19 
20             person.UserName = "guozhiqi";
21             //person.UserPwd = "123";
22             Serialize<Person>(person, "person.xml");
23             Console.Read();
24         }
25 
26         /// <summary>
27         /// 序列化方法
28         /// </summary>
29         /// <typeparam name="T"></typeparam>
30         /// <param name="instace"></param>
31         /// <param name="fileName"></param>
32         public static void Serialize<T>(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     /// <summary>
43     /// 定义一个实体类 Person
44     /// </summary>
45     public class Person
46     {
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.ConsoleClient
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person person = new Person(25, Guid.NewGuid())
16             {
17                 Date = DateTime.Now
18             };
19 
20             person.UserName = "guozhiqi";
21             //person.UserPwd = "123";
22             Serialize<Person>(person, "person.xml");
23             Console.Read();
24         }
25 
26         /// <summary>
27         /// 序列化方法
28         /// </summary>
29         /// <typeparam name="T"></typeparam>
30         /// <param name="instace"></param>
31         /// <param name="fileName"></param>
32         public static void Serialize<T>(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     /// <summary>
43     /// 定义一个实体类 Person
44     /// </summary>
45     public class Person
46     {
47         private Guid _id;
48 
49         private DateTime _date;
50         //注意我们没有默认的构造函数
51         internal double Age { get; set; } //私有字段 年龄
52 
53         public Guid ID
54         {
55             get { return _id; }
56             private set
57             {
58                 _id = value;
59             }
60         } //公有的随机数
61 
62         public DateTime Date
63         {
64             private set
65             {
66                 _date = value;
67             }
68             get
69             {
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.ConsoleClient
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             Person person = new Person(25, Guid.NewGuid())
16             {
17                 Date = DateTime.Now
18             };
19 
20             person.UserName = "guozhiqi";
21             //person.UserPwd = "123";
22             Serialize<Person>(person, "person.xml");
23             Console.Read();
24         }
25 
26         /// <summary>
27         /// 序列化方法
28         /// </summary>
29         /// <typeparam name="T"></typeparam>
30         /// <param name="instace"></param>
31         /// <param name="fileName"></param>
32         public static void Serialize<T>(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     /// <summary>
43     /// 定义一个实体类 Person
44     /// </summary>
45     public class Person
46     {
47         private Guid _id;
48 
49         private DateTime _date;
50         //注意我们没有默认的构造函数
51         internal double Age { get; set; } //私有字段 年龄
52 
53         public Guid ID
54         {
55             get { return _id; }
56             private set
57             {
58                 _id = value;
59             }
60         } //公有的随机数
61 
62         public DateTime Date
63         {
64             private set
65             {
66                 _date = value;
67             }
68             get
69             {
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的序列化器也是很有帮助的。

下一篇wcf 基础教程 契约 Contract 控制xml输出 数据契约DataContract序列化前身 XmlSerializer xml序列化

 

posted @ 2013-03-21 22:56  baidixing  阅读(1483)  评论(0编辑  收藏  举报