1、 DataContractSerializer支持的类型.................................................................................................................. 2

1.1. 用[DataContract]属性标记的类型........................................................................................................................ 2

1.2. .net 原生类型............................................................................................................................................................... 2

1.3. 用[Serializable]属性标记的类型........................................................................................................................... 2

1.4. 枚举类型.......................................................................................................................................................................... 3

1.5. 呈现为xml的类型....................................................................................................................................................... 3

1.6. 集合类型.......................................................................................................................................................................... 3

2、 定义需要使用DataContractSerializer序列化的类....................................................................................... 3

2.1. DataContract属性的参数........................................................................................................................................ 3

2.2. DataMember属性的参数........................................................................................................................................ 4

2.3. [DataContract]属性标识的类型跟XmlSerializer序列化器的类型的不同............................................ 4

2.4. 准备待序列化的用 DataContract标识类............................................................................................................ 5

2.5. 准备待序列化的用DataContract标识类型的对象........................................................................................... 7

3、 使用DataContractSerializer类不同的方法序列化用DataContract标识类型的对象................... 7

3.1. 使用WriteObject(Stream stream, object graph)方法序列化............................................................. 8

3.2. 使用WriteObject(XmlDictionaryWriter writer, object graph)方法序列化................................ 10

3.2.1. CreateTextWriter方法..................................................................................................................................... 10

3.2.2. CreateMtomWriter方法................................................................................................................................. 13

3.2.3. CreateBinaryWriter方法................................................................................................................................ 18

3.3. DataContractSerializer序列化总结................................................................................................................ 19

在WCF中,DataContractSerializer是默认的序列化器,不过WCF中还有一个叫NetDataContractSerializer的序列化器,它跟DataContractSerializer一样也是从XmlObjectSerializer类继承。NetDataContractSerializer跟DataContractSerializer一个主要的不同是:NetDataContractSerializer序列化后的xml中包含了.net的类型信息,反序列化时必须要被反序列化为同样类型的对象,这点跟BinaryFormatter和SoapFormatter这两个序列化器类似。DataContractSerializer序列化后的xml中则不包含.net的类型信息,通用性和交互性更好。在实际应用中DataContractSerializer是WCF的默认序列化器,绝大多数情况下都是使用DataContractSerializer,下面我们只对DataContractSerializer做详细介绍。

本文完整测试源代码下载: DataContractSerializer.rar 

一般情况下WCF的基础结构(infrastructure)会调用DataContractSerializer对要传输的数据对象就行序列化,不过也有特殊情况,WCF会调用别的序列化器序列化数据对象。

当遇到数据类成员中标记有类似[XmlAttribute]属性这样的细致设置生成xml格式的属性时,这时WCF infrastructure需要调用XmlSerializer序列化器来序列化这样的数据对象,通过在一个方法前加上[XmlSerializerFormat]属性值是WCF infrastructure调用XmlSerializer序列化器。

[XmlSerializerFormat]属性可以标记一个方法,表示这个方法使用XmlSerializer序列化器。

[XmlSerializerFormat]属性也可以用来标记一个ServiceContract,表示整个ServiceContract都用XmlSerializer序列化器。

比如:

[ServiceContract]

public interface IAirfareQuoteService

{

[OperationContract]

[XmlSerializerFormat]

float GetAirfare(Itinerary itinerary, DateTime date);

}

public class Itinerary

{

public string fromCity;

public string toCity;

[XmlAttribute]

public bool isFirstClass;

}

1、DataContractSerializer支持的类型

DataContractSerializer支持绝大多数的可序列化的类型的序列化,具体包括:

1.1.   用[DataContract]属性标记的类型

这种类型是为DataContractSerializer定制的可序列化类型,DataContractSerializer对这种了类型提供最全面的支持。

1.2.   .net 原生类型

.net内建的一些简单的数据类型,比如:Byte, SByte, Int16,string等,还有一些不是原生类型但是可以视作原声类型的类型,比如:DateTime, TimeSpan, Guid, Uri。

1.3.   用[Serializable]属性标记的类型

这是为rometing准备的序列化类型,被DataContractSerializer完全支持。

使用DataContractSerializer序列化前面SoapFormatter序列化的那个例子得到的结果:

把这个序列化后的结果跟直接使用SoapFormatter序列化的结果比较一下有以下几点不同:

l  DataContractSerializer序列化是面向数据的,不保留对象之间的引用关系,所以虽然address1和address2指向的是同一个对象,但DataContractSerializer还是分别给他们赋了同样的值。

l  不再包含.net的类型相关的信息,类的全限定名、版本、区域、KeyToken等等。

1.4.   枚举类型

1.5.   呈现为xml的类型

类型本身就可以呈现为xml的类型,比如XmlElement ,XmlNode,或者ADO.NET相关的类型,比如DataTable , DataSet。这些类型序列化时会被直接以xml的形式呈现。

1.6.   集合类型

可被序列化的类型组成的集合类型也被支持。

2、定义需要使用DataContractSerializer序列化的类

自定义一个类或结构,这个类或结构:

首先,在类前面加上[DataContract],表示这个类包含有数据契约

其次,在需要对外暴露的成员用[DataMember]属性标记。在这个类中,只有被标记为[DataMember]的成员才会被序列化,不管成员是公有的还是私有的。

2.1.   DataContract属性的参数

[DataContract]可以用来描述一个class或者struct,可以带有两个参数:

l  Name

指定这个数据契约对外的类型名,如果没有指定此参数,缺省的就是类名。

l  Namespace

指定这个类被序列化为xml后的Root元素的名称空间,为URI形式。

如果没有指定此参数,缺省名称空间为:“http://schemas.datacontract.org/2004/07/这个类的.net类名称空间”

比如有这样一个标记为DataContract的类型:

namespace DataContractSerializerTest.Myspace

{

[DataContract]

class Person

{

//代码

}

}

这个类型的默认名称空间就是http://schemas.datacontract.org/2004/07/DataContractSerializerTest.Myspace

2.2.   DataMember属性的参数

[DataMember]可以用来描述Property和field,不管这个成员是public或者private,只要标记了[DataMember],这个成员就会被序列化。

[DataMember]属性可以带有以下几个参数:

l  Name

指定这个成员对外暴露的名称,如果没有指定此参数,缺省的就是此Property或field名。

l  Isrequired

这个参数告诉DataContractSerializer序列化器把xml数据反序列化为这个类型时,这个成员是不是必须有的。此属性在反序列化时起作用。

l  Order

指示这个成员被序列化后的顺序。此属性在序列化时起作用。如果在一个类中既存在用Order参数约束的成员,又有没用Order参数约束的成员,那么序列化后没有Order参数约束的成员排在Order参数约束的成员之前。

l  EmitDefaultValue

此参数告诉DataContractSerializer序列化器,在序列化时如果这个数据成员的值是这个成员类型的缺省值(比如成员是int类型,缺省值为0,如果成员是个引用类型,缺省值为null),是否将此成员序列化。=true时表示要序列化,=false时表示不序列化,此参数的默认值为true。

当引用类型的成员为null时,此参数设置为true时,这个成员被序列化后的xml表现为xsi:nil="true",比如:

<Name xsi:nil="true" />

2.3.   [DataContract]属性标识的类型跟XmlSerializer序列化器的类型的不同

这两种类型都是面向数据的,都不关心数据的.net类型信息。但是他们还是有所不同:

l  DataContract的类型只能把用DataMember标识的成员序列化成element形式,不能序列化为attribute,如果一定要把数据成员序列化为attribute形式就需要用使用XmlSerializer序列化器。

l  DataMember属性可以通过order参数指定此成员序列化后的出现顺序,用于XmlSerializer序列化器的XmlElement属性无此功能。

l  DataContract的类型只要在成员前加上DataMember标记,不管成员是public或private的,都会被序列化,XmlSerializer序列化器只序列化public的成员。

l  DataContract的类型的成员的名称空间跟整个类的一致,成员不能独立设置自己的名称空间(DataMember属性没有名称空间参数),XmlSerializer序列化器允许成员拥有自己的名称空间(XmlAttribut和XmlElement都有namespage参数设置成员独立的名称空间)。

2.4.   准备待序列化的用 DataContract标识类

设计一个的Person类型,其中包含五个成员,binaryBuffer1、binaryBuffer2、name、address、age,其中name和address又分别是DataContract标识的简单类型。

binaryBuffer1和 binaryBuffer2是byte[]类型的数据,表示二进制的数据,可能包含任何非二进制的对象,比如图片、声音等等。这里放两个二进制的数据为了测试不同长度的二进制数据在DataContractSerializer不同的方法中会被如何处理。

[DataContract(Namespace = "htttp://chnking")]

class Person

{

private Name name;

private Address address;

private int age;

[DataMember]

public byte[] binaryBuffer1;

[DataMember]

public byte[] binaryBuffer2;

public Person(Name name, Address address, int age)

{

this.name = name;

this.address = address;

this.age = age;

}

[DataMember(Order = 2)]

public int Age

{

get { return age; }

set { age = value; }

}

[DataMember(EmitDefaultValue = true, Order = 0)]

public Name Name

{

get { return name; }

set { name = value; }

}

[DataMember(Order = 1)]

public Address Address

{

get { return address; }

set { address = value; }

}

}

[DataContract(Namespace = "htttp://chnking")]

class Name

{

private string firstname;

private string lastname;

public Name(string firstname, string lastname)

{

this.firstname = firstname;

this.lastname = lastname;

}

[DataMember]

public string Lastname

{

get { return lastname; }

set { lastname = value; }

}

[DataMember]

public string Firstname

{

get { return firstname; }

set { firstname = value; }

}

}

[DataContract(Namespace = "htttp://chnking")]

class Address

{

private string city;

private string postcode;

private string street;

public Address(string city, string postcode, string street)

{

this.city = city;

this.postcode = postcode;

this.street = street;

}

[DataMember]

public string Street

{

get { return street; }

set { street = value; }

}

[DataMember]

public string Postcode

{

get { return postcode; }

set { postcode = value; }

}

[DataMember]

public string City

{

get { return city; }

set { city = value; }

}

}

2.5.   准备待序列化的用DataContract标识类型的对象

实例化一个Person的对象,后续步骤将要序列化这个对象。

注意,这里给第一个二进制数据赋了一个20字节长数据,第二个二进制数据赋了一个768字节长度的数据,为什么这么赋值,后面会有说明。

//构造一个需要序列化的DataContract的对象

Person person = new Person(new Name("比尔", "盖茨"),

new Address("shenzhen", "518000", "fuqiang road"),

40);

//用一个重复的值填充第一个二进制数据bufferbytes1

int size1 = 20;

byte[] bufferbytes1 = new byte[size1];

for (int i = 0; i < size1; i++)

{

//65表示ASCII码的A

bufferbytes1[i] = 65;

}

person.binaryBuffer1 = bufferbytes1;

//用一个重复的值填充第二个二进制数据bufferbytes2

int size2 = 768;

byte[] bufferbytes2 = new byte[size2];

for (int i = 0; i < size2; i++)

{

//66表示ASCII码的B

bufferbytes2[i] = 66;

}

person.binaryBuffer2 = bufferbytes2;

3、使用DataContractSerializer类不同的方法序列化用DataContract标识类型的对象

实例化一个DataContractSerializer序列化器,DataContractSerializer序列化器跟XmlSerializer序列化器一样,没有缺省构造方法,至少需要提供要序列化对象的类型参数。

//准备DataContractSerializer序列化器

DataContractSerializer dcs = new DataContractSerializer(typeof(Person));

DataContractSerializer序列化对象的方法是WriteObject,这个WriteObject主要有两个重载,WriteObject(Stream stream, object graph)和WriteObject(XmlDictionaryWriter writer, object graph)。

下面对这两种序列化方法分别讨论。

3.1.   使用WriteObject(Stream stream, object graph)方法序列化

这个方法跟XmlSerializer序列化器的Serialize方法类似,是把序列化的对象的xml字符串直接编码序列化为字节流,而且跟XmlSerializer一样,从字符串到字节流的使用UTF8进行编码。

Stream(dcs, person);

private static void Stream(DataContractSerializer dcs, Person person)

{

//构造用于保存序列化后的DataContract对象的流

MemoryStream contractDataStream = new MemoryStream();

//将DataContract对象序列化到流

dcs.WriteObject(contractDataStream, person);

//为了查看序列化到流中的内容,将流内容读取出来并用UTF8解码为字符串

byte[] contractDataByte = new byte[contractDataStream.Length];

contractDataStream.Position = 0;

contractDataStream.Read(contractDataByte, 0, (int)contractDataStream.Length);

string contractDataString = Encoding.UTF8.GetString(contractDataByte);

//将流反序列化为DataContract对象

contractDataStream.Position = 0;

person = (Person)dcs.ReadObject(contractDataStream);

}

这部分代码跟XmlSerializer部分的类似,不加更多的说明了。

序列化后的xml这样的:

WriteObject(Stream stream, object graph)方法序列化有以下特点:

l  DataContractSerializer首先把对象序列化为一个xml 形式的字符串。

l  对于其中的二进制数据,不管多大长度,一律转成base64编码的字符串。本例中bufferbytes2被设置为768字节长度,序列化结果是一串base64的编码,又测试了把bufferbytes2长度增加为7680个字节,序列化的结果仍然是一串更长的base64的编码。

l  DataContractSerializer然后把序列化后的xml形式的字符串以UTF8编码(这是默认的编码,并且没有提供可以使用别的编码的接口,所以对其解码也必须使用UTF8,这点跟XmlSerializer和SoapFormatter序列化器一样)对其进行编码,转换到字节流,最终保存到一个流对象中,本例中就是MemoryStream。可以通过对字节流进行UTF8解码得到xml的字符串,就是上图看到的结果。

反序列化后的Person对象是这样的:

3.2.   使用WriteObject(XmlDictionaryWriter writer, object graph)方法序列化

这个方法是DataContractSerializer序列化器把对象序列化为xml字符串后,不直接对xml进行编码转换成字节流,而是把xml写入到XmlDictionaryWriter,XmlDictionaryWriter提供了多种对xml进行编码的选择,最终再由XmlDictionaryWriter把编码后的xml写入到目标流中。

XmlDictionaryWriter提供了三类静态方法,构造三种不同类型的XmlDictionaryWriter,分别对xml进行不同的编码操作。

它们分别是:

3.2.1.   CreateTextWriter方法

CreateTextWriter方法构建的XmlDictionaryWriter写入器,这样的写入器跟WriteObject(Stream stream, object graph)方法的作用类似,也是直接把xml的内容直接编码为字节流,但是允许选择编码时使用哪种编码。

XmlDictionaryWriter的CreateTextWriter方法主要下面两种重载形式。

public static XmlDictionaryWriter CreateTextWriter (Stream stream)

public static XmlDictionaryWriter CreateTextWriter (Stream stream, Encoding encoding)

第一个重载方法没有Encoding参数,默认采用UTF8进行编码,这样第一个方法的作用其实跟WriteObject(Stream stream, object graph)方法一样。

第二个重载方法有Encoding参数,可以指定在编码时采用何种编码,是Unicode还是UTF8等。下面的示例代码使用第二个重载方法,指定UTF8编码。

XmlDictionaryCreateTextWriter(dcs, person);

private static void XmlDictionaryCreateTextWriter(DataContractSerializer dcs, Person person)

{

//构造用于保存序列化并编码后的DataContract对象的流

MemoryStream contractDataStream = new MemoryStream();

//新建一个CreateTextWriter方法构建的指定Unicode的XmlDictionaryWriter对象

XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(contractDataStream, Encoding.UTF8);

//调用WriteObject方法将对象通过XmlDictionaryWriter序列化并编码

dcs.WriteObject(xdw, person);

//将序列化并编码后的字节流写入到原始流对象

xdw.Flush();

contractDataStream.Position = 0;

//为了查看序列化到流中的内容,将流内容读取出来并用Unicode解码为字符串

byte[] contractDataByte = new byte[contractDataStream.Length];

contractDataStream.Read(contractDataByte, 0, (int)contractDataStream.Length);

string contractDataString = Encoding.UTF8.GetString(contractDataByte);

//将流反序列化为DataContract对象

contractDataStream.Position = 0;

XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(contractDataStream, Encoding.UTF8, new XmlDictionaryReaderQuotas(), new OnXmlDictionaryReaderClose(OnReaderClose));

person = (Person)dcs.ReadObject(reader);

reader.Close();

}

public static void OnReaderClose(XmlDictionaryReader reader)

{

return;

}

序列化后,contractDataStream中获得的序列化后的数据长度为1385字节。

将contractDataStream中的字节流用UTF8解码,得到的内容跟前面的WriteObject(Stream stream, object graph)方法序列化后的结果一模一样:

再用Encoding.Unicode编码测试,序列化后,contractDataStream中获得的序列化后的数据长度为2756字节,明显比前面使用UTF8编码得到的结果长的多。这是很正常的结果,因为一般ASCII可表示的字符,使用UTF8编码也是用一个字节表示,并跟ASCII编码兼容,而UTF-16编码会把所有字符都编码为2个字节,所以使用Encoding.Unicode编码后的结果会大很多,几乎是UTF的2倍(因为测试的内容字符绝大多数是ASCII可表示的字符)

将contractDataStream中的字节流用Encoding.Unicode解码,查看内容为,解码后的内容跟UTF8解码后的内容也是一致的:

实际测试CreateTextWriter方法中的Encoding参数只能为:Encoding.Unicode、Encoding.UTF8和Encoding.BigEndianUnicode 三种。

CreateTextWriter方法构建的XmlDictionaryWriter写入器有以下特点:

l  DataContractSerializer首先把对象序列化为xml的各个元素写入到XmlDictionaryWriter对象。

l  XmlDictionaryWriter把内容最后写入到基础流的时候,对于二进制数据类型的元素,不管多大长度,一律转成base64编码的字符串。对于字符串类类型的元素以指定的编码对其进行编码,转换到字节流,最终保存到一个流对象中,本例中就是MemoryStream。

序列化时,使用XmlDictionaryWriter的CreateTextWriter方法建立的XmlDictionaryWriter对象作为写入器,反序列化时,相应的就要用XmlDictionaryReader的CreateTextReader建立的XmlDictionaryReader对象来作为读取器。

CreateTextReader方法签名如下:

CreateTextReader(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose);

参数说明:

stream – 保存序列化后数据的流对象。

encoding – 反序列化时使用的编码,应该跟序列化时使用的编码一致。

quotas – 指定XmlDictionaryReader对象的一些极限值,比如可以读取的stream的最长长度等等。XmlDictionaryReaderQuotas的默认构造方法对这些极限值构造了一个比较安全的缺省值,比如MaxArrayLength = 16384,可以反序列化数据的最长长度。当序列化后的stream中的字节长度长于MaxArrayLength设定的值,反序列化时将会抛出异常。

onClose – delegate类型,指定一个方法,在XmlDictionaryReader关闭时触发

3.2.2.   CreateMtomWriter方法

前面CreateTextWriter方法构造的XmlDictionaryWriter写入器,处理二进制数据的方式是把二进制数据转成base64编码的字符串,这个处理方法在一般情况下是个不错的方案,但是base64编码会增加编码后的长度,在二进制数据比较长的情况下,base64编码带来的长度增加将不能忽视。

Base64编码将每3个字节的数据拆散重组为4字节可表示简单字符的数据,编码后的数据长度比为3:4,如果数据长度不长,增加的长度影响不大,但是如果原来的二进制数据就比较大,比如300K,编码后的数据将会是400K,再如果原来是30M,编码后为40M,多出来的数据量难以忽视。

鉴于这种情况,W3C制定了XOP(XML-binary Optimized Packages,XOP)协议来解决二进制问题,XOP 提供一个机制,可选择性地提取要优化的信息,将其添加到多部分 MIME 消息中(其中也包括您的 SOAP 消息)并显式地对其进行引用。使用 XOP 的过程称为 MTOM(SOAP 消息传输优化机制——Message Transmission Optimization Mechanism)。

经过MTOM处理的数据,形式上为MIME(邮件的内容形式),XML 数据组成第一部分,而二进制数据视其长度的不同或者被编码成base64放在xml中,或者作为附加部分添加到xml部分的后面。

CreateMtomWriter用来构造XmlDictionaryWriter最常用的方法重载是:

CreateMtomWriter(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo);

参数说明:

stream – 保存序列化后数据的流对象。

encoding – 序列化时使用的编码。

maxSizeInBytes – 将被缓冲到XmlDictionaryWriter写入器的数据最大字节数

startInfo – 在生成的MIME消息的ContentType增加一个名称为start-info的header,header的值就是这个参数提供的。

看一下用CreateMtomWriter构造的XmlDictionaryWriter序列化前面例子的代码:

XmlDictionaryCreateMtomWriter(dcs, person);

private static void XmlDictionaryCreateMtomWriter(DataContractSerializer dcs, Person person)

{

//构造用于保存序列化并编码后的DataContract对象的流

MemoryStream contractDataStream = new MemoryStream();

//新建一个CreateMtomWriter方法构建的指定Unicode的XmlDictionaryWriter对象

XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateMtomWriter(contractDataStream, Encoding.UTF8, 2048000, "text/xml");

//调用WriteObject方法将对象通过XmlDictionaryWriter序列化并编码

dcs.WriteObject(xdw, person);

//将序列化并编码后的字节流写入到原始流对象

xdw.Flush();

contractDataStream.Position = 0;

//为了查看序列化到流中的内容,将流内容读取出来并用Unicode解码为字符串

byte[] contractDataByte = new byte[contractDataStream.Length];

contractDataStream.Read(contractDataByte, 0, (int)contractDataStream.Length);

string contractDataString = Encoding.UTF8.GetString(contractDataByte);

//将流反序列化为DataContract对象

contractDataStream.Position = 0;

XmlDictionaryReader reader = XmlDictionaryReader.CreateMtomReader(contractDataStream, Encoding.UTF8, XmlDictionaryReaderQuotas.Max);

person = (Person)dcs.ReadObject(reader);

reader.Close();

}

生成的序列化后的数据用UTF8解码后看:

可以看出CreateMtomWriter构造的XmlDictionaryWriter将xml的内容转成了MIME的形式,下面仔细的分析一下MIME消息的构成。

XmlMtomWriter生成的MIME的消息一般分为三个部分:

l  MIME头

MIME-Version: 1.0  - 表示MIME的版本号

头部最重要的内容是Content-Type中的一些内容,它们是:

Content-Type: multipart/related – 表示是多部分的消息

type="application/xop+xml" – 表示是xop协议的MIME消息格式

Boundary – 部分之间的边界,这个值插在不同的部分之间以分割不同的部分内容

Start – 开始的那个部分的Content-ID,每个部分都有一个唯一的Content-ID标识。这里开始部分就是xml的部分。

l  第一部分(xml部分)

MIME头部跟第一部分之间有个空行,表示头部结束后面是部分的内容。

Boundary指定的字符串前面加上”—“字符是部分开始和结束的标识。

每个部分也分header和body部分,以空行分割。

Content-ID: <http://tempuri.org/0/633482733255804248> – 此部分的标识id

Content-Transfer-Encoding: 8bit – 表示body部分的内容可能是非ASCII编码的编码内容,这里xml的内容一般以UTF8或UTF16编码,使用了每个字节的所有8位。

Content-Type: application/xop+xml;charset=utf-8;type="text/xml" – 表示body的内容类型为xop的xml内容。

Body部分就是xml的内容,重点关注一下二进制数据binaryBuffer2的表现形式。

二进制binaryBuffer1数据部分被直接编码成base64字符串放在xml中。

binaryBuffer2部分的数据实际上从xml中剥离出来,作为一个单独的部分存在。看一下在xml中的binaryBuffer2:

<binaryBuffer2><xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F633482781614236973" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></binaryBuffer2>

其中href=cid:http%3A%2F%2Ftempuri.org%2F1%2F633482781614236973指向表示这部分二进制数据的部分的标识id,实际的二进制数据被放在指向的那个部分。

l  后续部分(二进制数据部分)

没有被直接放在第一部分xml中的内容都会被放置在随后的那些部分中,本例中就只有一个binaryBuffer2部分的数据被放置到后续部分。

同样这个部分有header和body部分,以空行分割。

Content-ID: http://tempuri.org/1/633482781614236973 - 就是xml部分中binaryBuffer2引用的id。

Content-Transfer-Encoding: binary – 表示body部分是二进制的未经编码的。

Content-Type: application/octet-stream – 内容类型为数据流。

在测试中我们把binaryBuffer2的数据长度设置为768,如果改成767,看看结果:

可以发现这时binaryBuffer2的数据也被编码成base64直接放入到xml中了。

也就是说,CreateMtomWriter构造的XmlDictionaryWriter是根据二进制数据的长度来决定数据如何表现,如果小于768则把数据编码为base64,如果大于等于768则把数据单独放到一个部分,并保持二进制数据的原样。

CreateMtomWriter方法构建的XmlDictionaryWriter写入器有以下特点:

l  DataContractSerializer首先把对象序列化为xml的各个元素写入到XmlDictionaryWriter对象。(这步跟CreateTextWriter方法构建的XmlDictionaryWriter一样,其实DataContractSerializer传到XmlDictionaryWrite的内容是一样的)

l  CreateMtomWriter方法构建的XmlDictionaryWriter写入器分析DataContractSerializer传进来的xml元素的内容,如果包含二进制的内容,则判断如果二进制的数据大于等于768字节的就把它放到一个单独部分保持二进制,如果小于依然保存在xml中以base64编码。

l  MTOM的header和Boundary部分边界符都使用UTF8编码,xml部分的编码由CreateMtomWriter方法中的encoding参数决定,最终将MTOM的内容根据各自的编码转换到字节流,保存到一个流对象中,本例中就是MemoryStream。

3.2.3.   CreateBinaryWriter方法

DataContractSerializer还提供了一种最高效的序列化方式,二进制序列化,把xml内容直接转成二进制的数据,不过这样的转换是微软自己的定义的,只能在.net环境下使用,跟别的技术不具有交互性。

看一下用CreateBinaryWriter构造的XmlDictionaryWriter序列化前面例子的代码:

XmlDictionaryCreateBinaryWriter(dcs, person);

private static void XmlDictionaryCreateBinaryWriter(DataContractSerializer dcs, Person person)

{

//构造用于保存序列化并编码后的DataContract对象的流

MemoryStream contractDataStream = new MemoryStream();

//新建一个CreateTextWriter方法构建的指定Unicode的XmlDictionaryWriter对象

XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateBinaryWriter(contractDataStream);

//调用WriteObject方法将对象通过XmlDictionaryWriter序列化并编码

dcs.WriteObject(xdw, person);

//将序列化并编码后的字节流写入到原始流对象

xdw.Flush();

contractDataStream.Position = 0;

//为了查看序列化到流中的内容,将流内容读取出来并用UTF8解码为字符串

byte[] contractDataByte = new byte[contractDataStream.Length];

contractDataStream.Read(contractDataByte, 0, (int)contractDataStream.Length);

string contractDataString = Encoding.UTF8.GetString(contractDataByte);

//将流反序列化为DataContract对象

contractDataStream.Position = 0;

XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(contractDataStream, new XmlDictionaryReaderQuotas());

person = (Person)dcs.ReadObject(reader);

reader.Close();

}

序列化后的结果是二进制的,不具可读性,下面试着用UTF8编码强行把二进制的数据转成字符串看一下:

本例中,序列化后的二进制数据长度为1010字节,前面使用CreateTextWriter方法并使用UTF8编码的序列化后的数据长度为1385。可见二进制序列化后的数据长度最短。

3.3.   DataContractSerializer序列化总结

DataContractSerializer本身完成了把要序列化的对象序列化为xml的形式,之后再由不同的编码方案对这个xml进行编码,最后形成完全序列化的数据流。

DataContractSerializer实际上提供了四种编码方案,其中WriteObject(Stream stream, object graph)直接序列化到流和CreateTextWriter方法构建的XmlDictionaryWriter这两种,是直接把xml字符进行编码,只是WriteObject(Stream stream, object graph)使用固定的UTF8编码,CreateTextWriter方法构建的XmlDictionaryWriter可以选择序列化使用的编码。

CreateMtomWriter方法构建的XmlDictionaryWriter是把对象的xml编码为MTOM的形式。

CreateBinaryWriter构造的XmlDictionaryWriter是把对象的xml编码为二进制的形式。

Xml形式具有最佳的互操作性,适应面最广。

MTOM的形式适合传输含有二进制大数据的对象。

二进制的形式传输效率最高,但是不具互操作性,只能用于.net环境的交换。

转:http://www.cnblogs.com/chnking/archive/2008/06/06/1215411.html

WCF系列(五) -- 也谈序列化(下)相关推荐

  1. WCF编程系列(五)元数据

    WCF编程系列(五)元数据 示例一中我们使用了scvutil命令自动生成了服务的客户端代理类: svcutil http://localhost:8000/?wsdl /o:FirstServiceC ...

  2. mysql分布式事务wcf_[转载]WCF系列_分布式事务(下)

    浏览到chnking的WCF的分布式事务处理不错,转载过来分享一下.1. WCF分布式事务例子这里也用转账的例子说事. 用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B ...

  3. WCF系列(1)—— CustomBehavior 入门

    由于最近工作一直在做wcf平台上的开发,所以决定先结合自己平时工作中的经验实践写一个WCF的系列,希望能对大家有所帮助. 首先,说到WCF,就不得不提Endpoint这个概念,而Endpoint则由A ...

  4. 【转】五、谈扩展方法的理解

    [转]五.谈扩展方法的理解 为什么要用扩展方法 在说什么是扩展方法之前我们先来说说为什么要用扩展方法. 首先我们定义一个 Person 类: public class Person {/// < ...

  5. 数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引

    数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引 [建立一个搜索引擎大致需要做这样几件事:自动下载尽可能多的网页:建立快速有效的索引:根据相关性对网页进行公平准确的排序.我们在介绍 Googl ...

  6. WCF系列(一)BasicHttpBinding 和 WsHttpBinding 的不同点

    aaaaaaaaaaaaaaaaaa WCF系列(一)[翻译]BasicHttpBinding 和 WsHttpBinding 的不同点 2010-02-21 12:23 by Virus-Beaut ...

  7. idea学习系列五之debug及插件的使用

    idea学习系列五之debug及插件的使用 上一篇,介绍了maven及服务器的使用,这里将介绍idea中debug及插件的使用.在实际开发中debug是最常用的了,而且idea相比于eclipse中的 ...

  8. Silverlight Blend动画设计系列五:故事板(StoryBoards)和动画(Animations)

    原文:Silverlight & Blend动画设计系列五:故事板(StoryBoards)和动画(Animations) 正如你所看到的,Blend是一个非常强大的节约时间的设计工具,在Bl ...

  9. 智能路由器-OpenWRT 系列五 (NAS-SMB家庭共享)

    NAS是我觉得家里必不可少的一台设备,NAS 全称 Network Attached Storage,即网络附加存储服务器,它通过自身的操作系统和 SMB/NFS/CIFS/FTP 等多种通信协议来给 ...

最新文章

  1. 如何在TensorFlow中用深度学习修复图像
  2. Linux xsync分发脚本完整内容
  3. jsonp react 获取返回值_谈谈对 React 新旧生命周期的理解
  4. Android知识体系框架
  5. Hive入门(三)分桶
  6. 产品设计的基本原则是什么
  7. SCADA数采HAAS数控系统现场实操步骤
  8. Scala下载和配置
  9. 推荐一款PDF阅读工具Apabi Reader
  10. 如何实现基于 RADIUS 协议的双因子认证 MFA?
  11. 驻波检测及双向耦合器参数说明
  12. 课堂作业:评估手机输入法——讯飞
  13. N32926 24小时连续测试720p, h.264 IPCAM功能,为什么基本不发热呢?
  14. 所有人都能看懂的华为交换机vlan配置
  15. 距离(distance)算法小结
  16. 第059篇:高分二号遥感影像预处理流程(ENVI5.3.1平台+ENVI App Store中最新的中国国产卫星支持工具)
  17. testing众测学院测试用例考核题目及答案
  18. 计算机组成原理sltu指令,计算机组成原理第二次作业题及答案.doc
  19. Django教程 —— 初步完善图书管理系统
  20. 《c语言程序设计》实验报告,C语言程序设计实验实验报告_wenkub

热门文章

  1. 经典C语言程序100例之八零
  2. 1.18.3.Flink Catalog介绍、Catalog 定义、Catalog 的实现、Catalog 使用举例
  3. 1.18.2.Table APISQL(概念与通用API、两种计划器(Planner)的主要区别、创建 TableEnvironment、临时表、永久表、创建表、虚拟表、Connector 等)
  4. 事件时间/处理时间/进入时间(Event Time / Processing Time / Ingestion Time)
  5. Spark SQL概述,DataFrames,创建DataFrames的案例,DataFrame常用操作(DSL风格语法),sql风格语法
  6. Scala类的继承,抽象类定义,接口定义
  7. Flume监听文件夹中的文件变化,并把文件下沉到hdfs
  8. 项目中的一个AOP的编写案例(配置+案例)
  9. adguard没有核心 core no_业主装修最后悔的五个地方!没有之一
  10. lotus表单域怎么获取视图结果_翻译案例 | 商业推广类文章怎么翻?如何修改译文?...