本篇是上篇的续,如果各位看官不妨从上篇看起,点此进入上篇。
上篇说到使用NetDataContractSerializer序列化对象时,会在xml上添加许多附加的attribute。这些attribute有一部分是为了完善序列化后内容的类型信息,比如z:Type和z:Assembly,而另一个作用鄙人买了个关子,留到这回来解释了~。
还是用示例来开篇吧。在这里对上回的示例做一些扩展:
namespace ServiceInterface


{
[DataContract]
public class Sub

{
public Sub()

{ }
public Sub(int id, string name)

{
Id = id;
Name = name;
}

[DataMember]
public int Id;

[DataMember]
public string Name;

}

[DataContract]
public class Root

{
public Root()

{ }
public Root(int id, string name, Sub sub)

{
Id = id;
Name = name;
Sub1 = sub;
Sub2 = sub;
}

[DataMember]
public int Id;

[DataMember]
public string Name;

[DataMember]
public Sub Sub1;

[DataMember]
public Sub Sub2;

}
}

相比上回的示例,这里多了一个Root类,而这个Root类又有两个成员Sub1,Sub2,都是Sub类型了。这是作甚?接着看就明白了~
还是先用DataContractSerializer来序列化Root对象,但这回于上回有些不同,序列化之后,还把它反序列化回来了:
Sub sub = new Sub(10, "ten");
Root root = new Root(100, "root", sub);

DataContractSerializer dcs = new DataContractSerializer(typeof(Root));
MemoryStream stream = new MemoryStream();

dcs.WriteObject(stream, root);

byte[] buf = stream.ToArray();
string str = Encoding.UTF8.GetString(buf, 0, buf.Length);

stream.Seek(0, SeekOrigin.Begin);
Root rt1 = (Root)dcs.ReadObject(stream);
执行上面这段代码可以发现序列化后的内容为:
<Root xmlns="http://schemas.datacontract.org/2004/07/ServiceInterface" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Id>100</Id>
<Name>root</Name>
<Sub1>
<Id>10</Id>
<Name>ten</Name>
</Sub1>
<Sub2>
<Id>10</Id>
<Name>ten</Name>
</Sub2>
</Root>
而反序列化后呢?细心XD可能已经看出了点端倪~什么?你没发现?再step debug一遍看看?
对了!在序列化之前Root对象的Sub1和Sub2引用的是同一个Sub对象,而反序列化之后,这个field变成分别引用两个不同对象了(虽然内容完全相同)!这还得了!!在讲究原汁原味的今天,怎么能容忍这种事的发生!这可不是俺夸大其词,假如有这么种情况,Root对象有对Sub的引用,而Sub又有对Root对象的引用,到循环引用出现时,DataContractSerializer就没法处理这种情况了。这时NetDataContractSerializer就有了用武之地。
到这里,读者们估计能猜出上回没有解释的z:Id是何用意了。没错,它就是用来解决对象引用问题的。对上文中的Root对象使用NetDataContractSerializer来序列化,并反序列化:
NetDataContractSerializer ndcs = new NetDataContractSerializer();
MemoryStream nstream = new MemoryStream();

ndcs.WriteObject(nstream, root);

byte[] nbuf = nstream.ToArray();
string nstr = Encoding.UTF8.GetString(nbuf, 0, nbuf.Length);

nstream.Seek(0, SeekOrigin.Begin);
Root rt2 = (Root)ndcs.ReadObject(nstream);
序列化后的xml为:
<Root z:Id="1" z:Type="ServiceInterface.Root" z:Assembly="ServiceInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/ServiceInterface" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>100</Id>
<Name z:Id="2">root</Name>
<Sub1 z:Id="3">
<Id>10</Id>
<Name z:Id="4">ten</Name>
</Sub1>
<Sub2 z:Ref="3" i:nil="true" />
</Root>
这回,反序列化后的对象可以说无论在形式上还是语义上都与原对象一致了。它是怎么做到的呢?注意看,Sub2元素的中z:Ref为3,而Sub1元素z:Id的值为3。这意味着:反系列化过程会为Sub1创建一个对象,而Sub2仅仅只会引用Sub1。最后还要指出一点,NetDataContractSerializer会为每个引用类型的对象分配一个z:Id。
关于WCF序列化的讨论先到这里,但我知道,这仅仅是个开始~