序列化DataContractSerializer
wcf基础教程之 数据契约的序列化DataContractSerializer
上一篇博客我们说明了数据契约的前身Xml的序列化,这次还是言归正传,回到wcf的技术上来,分析一下DataContractSerializer 。
首先我们必须了解wcf默认是通过xml进行数据传输,但是并不意味着就一定要用这种,还有轻量级的json。DataContractSerializer继承自XmlObjectSerializer,是可以直接对.Net对象进行序列化操作,但是DatacontractSerializer的使用更加方便。
要使一个类成为数据契约,我们必须要通过DataContractAttribute进行标注,然后通过应用DataMemberAttribute特性来标注它的属性或字段,才可以让这个类成为一个数据契约,在网络间进行传输。
DataContract的属性很少,但是都特别有用,在序列化的时候有一个属性 IsReference ,表示在进行序列化的时候是否要保持对象现有的引用结构,默认值为false。 这个属性在我们进行序列化的时候会举例说明其中的区别。
DataMember的属性也很少,IsRequired 表示是否是必须的,默认值为false。这个属性是什么意思呢?就是说在序列化的时候这个属性如果不提供,则会报错,或者在反序列化的时候,如果这个属性没有,那么也会报错,总之就是必须这个属性存在。
DataMember还有一个属性EmitDefaultValue,表示数据成员的值等于默认值的情况下,是否还需要将其序列化到xml中,默认值为true,表示总是将其序列化。
我提出一个问题?就是如何计算wcf的默认可以处理的对象个数?因为如果我们创建很多的对象进行序列化,那么可能会出现一个超出处理对象最大值的异常信息?这个也是我们必须要考虑的。
在数据契约中,还存在一种特殊的,我们叫做已知类型 KnownType。因为wcf在序列化和反序列化的时候,必须明确的知道对象的类型。这个放到下一篇我们来讨论什么时候会用到已知类型。
定义一个数据契约是那么的简单,以至于我们总是认为wcf还是那么的容易。请记住这么一句话,师傅领进门,修行在个人。入门简单这是师傅的功劳,但是修行怎么样,就要看你自己的造化了。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Runtime.Serialization;
6
7 namespace Chinaer.WcfDemo.Contracts
8 {
9 [DataContract]
10 public class Person
11 {
12 [DataMember]
13 public string ID { get ; set ; }
14
15 public string UserName { get ; set ; }
16
17 [DataMember]
18 /// <summary>
19 /// 私有属性
20 /// </summary>
21 private string UserPwd { get ; set ; }
22
23 // 公有字段
24 [DataMember]
25 public string ParentName;
26 // 私有字段
27 [DataMember]
28 private string BrotherName;
29
30 }
31 }
我们仍然在控制台进行输出到xml文件查看他们的区别。
1 static void Main( string [] args)
2 {
3 Chinaer.WcfDemo.Contracts.PersonName person= new Contracts.PersonName(){
4 ID= " 1 " , ParentName= " parent " , UserName= " guohz "
5 };
6 DataContractSerialize<Contracts.PersonName>(person, " person.xml " );
7 Console.Read();
8 }
9 /// <summary>
10 /// DataContractSerializer序列化
11 /// </summary>
12 /// <typeparam name="T"></typeparam>
13 /// <param name="instance"></param>
14 /// <param name="fileName"></param>
15 public static void DataContractSerialize<T>(T instance, string fileName)
16 {
17 DataContractSerializer serializer = new DataContractSerializer( typeof (Chinaer.WcfDemo.Contracts.PersonName));
18 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
19 {
20 serializer.WriteObject(writer, instance);
21
22 }
23 Process.Start(fileName);
24 }
数据契约序列化除了序列化器不同之外,其他的代码步骤都相同。生成的xml文件。
1 < PersonName xmlns:i ="http://HdhCmsTestw3.org/2001/XMLSchema-instance" 2 xmlns ="http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.Contracts" > 3 < BrotherName i:nil ="true" /> 4 < ID > 1 </ ID > 5 < ParentName > parent </ ParentName > 6 < UserPwd i:nil ="true" /> 7 </ PersonName >
大家注意:上面生成的xml我们可以看出,无论是公有属性,还有私有属性或私有字段,只要加上了DataMember attribute,那么就会被序列化。
现在我们来想一下,如果有主从关系的两个类,最后序列化会生成什么呢?动手试试吧。
我们再次定义一个类继承自刚才定义的类,然后我们序列化子类,我们一起来查看结果。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Runtime.Serialization;
6
7 namespace Chinaer.WcfDemo.Contracts
8 {
9 [DataContract]
10 public class PersonName
11 {
12 [DataMember]
13 public string ID { get ; set ; }
14
15 public string UserName { get ; set ; }
16
17 [DataMember]
18 /// <summary>
19 /// 私有属性
20 /// </summary>
21 private string UserPwd { get ; set ; }
22
23 // 公有字段
24 [DataMember]
25 public string ParentName;
26 // 私有字段
27 [DataMember]
28 private string BrotherName;
29
30 }
31
32 [DataContract]
33 public class ParentName:PersonName
34 {
35 [DataMember]
36 public string Parent { get ; set ; }
37 }
38
39 }
序列化子类生成的xml为:
1 <ParentName xmlns:i= " http://HdhCmsTestw3.org/2001/XMLSchema-instance " 2 xmlns= " http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.Contracts " > 3 <BrotherName i:nil= " true " /> 4 <ID> 1 </ID> 5 <ParentName>parent</ParentName> 6 <UserPwd i:nil= " true " /> 7 <Parent>parent</Parent> 8 </ParentName>
对比父类生成的xml,我们可以得出以下结论:
父类的属性在序列化成xml的时候在子节点的前面。 序列化中xml元素的排序时按照字母顺序排序。 子类序列化会吧父类一起序列化,并且序列化到子类中对于第一点,可能这是我发现的一个规律,对于第二点,可能有朋友要说了,这只是看了一个就得出这个结论,有点草率,如果添加了order属性,那么肯定顺序会改变。对于第三个,应该说是常识,但是我在上面说过一个关于引用的问题,这个问题就和这个有关。
下面我们添加order属性,来再次查看生成的xml的结果。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Runtime.Serialization;
6
7 namespace Chinaer.WcfDemo.Contracts
8 {
9 [DataContract]
10 public class PersonName
11 {
12 [DataMember(Order = 2 )]
13 public string ID { get ; set ; }
14
15 public string UserName { get ; set ; }
16
17 [DataMember(Order = 1 )]
18 /// <summary>
19 /// 私有属性
20 /// </summary>
21 private string UserPwd { get ; set ; }
22
23 // 公有字段
24 [DataMember(Order = - 1 )]
25 public string ParentName;
26 // 私有字段
27 [DataMember(Order = 0 )]
28 private string BrotherName;
29
30 }
31
32 [DataContract]
33 public class ParentName : PersonName
34 {
35 [DataMember(Order = 1 )]
36 public string Parent { get ; set ; }
37 }
38
39 }
请注意上面的代码,我们为order赋值的时候,有赋值-1和0,子类我们赋值为1.那么根据order的作用,父类应该是按照order排序,子类的位置我们根据生成的xml文件来查看。
如果你是直接运行的话,那么你会得到如下的异常信息提示。
我们上面说过,特别注意我们为order赋值-1.其中我们的ParentName属性的order就是-1.如果我没记错的话,我们最好设置0以上的数字
1 <ParentName xmlns:i= " http://HdhCmsTestw3.org/2001/XMLSchema-instance " 2 xmlns= " http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.Contracts " > 3 <BrotherName i:nil= " true " /> 4 <UserPwd i:nil= " true " /> 5 <ID> 1 </ID> 6 <ParentName>parent</ParentName> 7 <Parent>parent</Parent> 8 </ParentName>
加了order属性之后,xml元素的顺序果然改变了,但是请注意最后一个,仍然是子类的元素。所以请注意这个规律就可以。
下面我们来一起了解一下,如果类之间有引用关系,那么如果保持引用和不保持引用状态生成的xml有哪些不同呢?
我们取消personName和parentName的父子关系,然后重修修改序列化,添加是否需要保持引用状态的bool值。
1 static void Main( string [] args)
2 {
3 Chinaer.WcfDemo.Contracts.ParentName person = new Contracts.ParentName()
4 {
5 Parent = " parent "
6 };
7 Chinaer.WcfDemo.Contracts.PersonName personName = new Contracts.PersonName()
8 {
9 ID = " id " ,
10 ParentName = person,
11 ParentName2 = person,
12 UserName = " guozhiqi "
13 };
14 DataContractSerialize<Contracts.PersonName>(personName, " person.xml " , true ); // 保持引用状态
15 DataContractSerialize<Contracts.PersonName>(personName, " person2.xml " , false ); // 不保持引用状态
16 Console.Read();
17 }
18 /// <summary>
19 /// DataContractSerializer序列化
20 /// </summary>
21 /// <typeparam name="T"></typeparam>
22 /// <param name="instance"></param>
23 /// <param name="fileName"></param>
24 public static void DataContractSerialize<T>(T instance, string fileName, bool preserveReference)
25 {
26 DataContractSerializer serializer = new DataContractSerializer( typeof (Chinaer.WcfDemo.Contracts.ParentName), null , int .MaxValue, false , preserveReference, null );
27 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
28 {
29 serializer.WriteObject(writer, instance);
30
31 }
32 Process.Start(fileName);
33 }
保持对象引用状态生成的xml文件为:
1 <PersonName xmlns:i= " http://HdhCmsTestw3.org/2001/XMLSchema-instance " 2 z:Id= " 1 " xmlns:z= " http://schemas.microsoft测试数据/2003/10/Serialization/ " 3 xmlns= " http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.Contracts " > 4 <ParentName2 z:Id= " 2 " > 5 <Parent z:Id= " 3 " >parent</Parent> 6 </ParentName2> 7 <BrotherName i:nil= " true " /> 8 <UserPwd i:nil= " true " /> 9 <ID z:Id= " 4 " >id</ID> 10 <ParentName z:Ref= " 2 " i:nil= " true " /> 11 </PersonName>
不保持对象引用状态生成的xml文件为:
1 <PersonName xmlns:i= " http://HdhCmsTestw3.org/2001/XMLSchema-instance " 2 xmlns= " http://schemas.datacontract.org/2004/07/Chinaer.WcfDemo.Contracts " > 3 <ParentName2><Parent>parent</Parent></ParentName2> 4 <BrotherName i:nil= " true " /> 5 <UserPwd i:nil= " true " /> 6 <ID>id</ID> 7 <ParentName> 8 <Parent>parent</Parent></ParentName> 9 </PersonName>
我对xml理解的不深,无法准确的说出其中的区别,但是我看到了在不保持对象引用状态的情况下,对象引用了几次就会出现几次。我们可以得出这么一个结论,在大数据量的情况下,保持对象的引用状态可以减少传输的数据量。
刚才那个问题我们还没有回答?就是对象的个数如何计算?为了回答这个问题,我们来看一个例子
请注意我们的序列化代码:
1 static void Main( string [] args)
2 {
3 Chinaer.WcfDemo.Contracts.ParentName person = new Contracts.ParentName()
4 {
5 Parent = " parent "
6 };
7 Chinaer.WcfDemo.Contracts.PersonName personName = new Contracts.PersonName()
8 {
9 ID = " id " ,
10 ParentName = person,
11 ParentName2 = person,
12 UserName = " guozhiqi "
13 };
14
15 List<Contracts.ParentName> listParent = new List<Contracts.ParentName> ();
16 for ( int i = 0 ; i <= 10000 ; i++ )
17 {
18 Contracts.ParentName parentName = new Contracts.ParentName()
19 {
20 Parent = i.ToString()
21 };
22 listParent.Add(parentName);
23 }
24
25 DataContractSerialize<List<Contracts.ParentName>>(listParent, " list.xml " , false );
26
27 // DataContractSerialize<Contracts.PersonName>(personName, "person.xml", true); // 保持引用状态
28 // DataContractSerialize<Contracts.PersonName>(personName, "person2.xml", false); // 不保持引用状态
29 Console.Read();
30 }
31 /// <summary>
32 /// DataContractSerializer序列化
33 /// </summary>
34 /// <typeparam name="T"></typeparam>
35 /// <param name="instance"></param>
36 /// <param name="fileName"></param>
37 public static void DataContractSerialize<T>(T instance, string fileName, bool preserveReference)
38 {
39 DataContractSerializer serializer = new DataContractSerializer( typeof (T), null , 50 , false , preserveReference, null );
40 using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))
41 {
42 serializer.WriteObject(writer, instance);
43
44 }
45 Process.Start(fileName);
46 }
请看代码中的50,表示的就是wcf每次最多处理的对象数,默认值为65536,我们设置为50就是为了出现那么久违的异常信息。这次我们序列化的事一个列表。
在异常信息中有一个属性MaxItemsInObjectGraph,表示的就是处理的最大对象数,如果出现了这个异常信息的话,我们就需要调高这个值。
如何处理才能解决这个异常信息呢?其实有多种方法:
首先第一个就是Servic eBehavior 有一个属性值MaxItemsInObjectGraph,就可以设置大小。
还有就是通过配置文件的方式。
1 <behaviors> 2 <serviceBehaviors> 3 <behavior name= " metaDataBehavior " > 4 <serviceMetadata httpGetEnabled= " true " /> 5 <dataContractSerializer maxItemsInObjectGraph= " 65536 " /> 6 </behavior> 7 </serviceBehaviors> 8 </behaviors>
在行为Behaviors节点下,有一个dataContractSerializer 可以通过它来设置对象的大小。
知道了如何来调整大小,那么我们应该如何计算对象的个数呢?
1 [DataContract]
2 public class Order
3 {
4 [DataMember]
5 public string UserName { get ; set ; }
6 [DataMember]
7 public string UserPwd { get ; set ; }
8 }
9 public class Exec
10 {
11 public void Execute()
12 {
13 List<Order> list = new List<Order> ();
14 for ( int i = 0 ; i < 10 ; i++ )
15 {
16 Order order = new Order() { UserName= " userName " };
17 list.Add(order);
18 }
19
20 }
21
22 }
比如说上面的代码,如果我们序列化list对象,那么相当于我们需要处理多少对象呢?
list对象本身算一个,每个order对象算一个,每个order对象的每个属性算一个,就是1+10+20=31 。序列化一个简单的列表就需要处理31个对象。
如果我们处理一个复杂的数据,那么就会更多了,所以一定要计算好,尽量不要超越设定的最大值。
数据契约序列化DataContractserializer 是xmlObjectSerializer的延伸,是简单化的处理。其中也有区别,例如xml序列化是按照元素的出现位置排序,而datacontractserializer是按照字母顺序排序。当然存在order属性的除外。
每天进步一点,一年就会进步一大步,十年就可以成功,君子当自强不息,君子当好好学习,每天进步
分类: Wcf
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于序列化DataContractSerializer的详细内容...