zoukankan      html  css  js  c++  java
  • 序列化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://www.w3.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://www.w3.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,我们可以得出以下结论:

    1. 父类的属性在序列化成xml的时候在子节点的前面。
    2. 序列化中xml元素的排序时按照字母顺序排序。
    3. 子类序列化会吧父类一起序列化,并且序列化到子类中

    对于第一点,可能这是我发现的一个规律,对于第二点,可能有朋友要说了,这只是看了一个就得出这个结论,有点草率,如果添加了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://www.w3.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://www.w3.org/2001/XMLSchema-instance" 
     2     z:Id="1" xmlns:z="http://schemas.microsoft.com/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://www.w3.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,表示的就是处理的最大对象数,如果出现了这个异常信息的话,我们就需要调高这个值。

    如何处理才能解决这个异常信息呢?其实有多种方法:

    首先第一个就是ServiceBehavior 有一个属性值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
  • 相关阅读:
    Halcon各个算子角度计算方式汇总
    阈值分割算子之OSTU算法
    Halcon18--深度学习应用问题记录
    Halcon18新技术
    Halcon之3D重建
    关于数组排序(顺序,逆序)
    封装jq的ajax
    h5在微信生成分享海报(带二维码)
    使用mintUI的总结
    时间戳转日期
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2988237.html
Copyright © 2011-2022 走看看