在SDK里讲到序列化的时候,有“二进制序列化”、“XML和SOAP序列化”这么两节,加之比较早就知道了BinaryFormatter和SoapFormatter这两个用于序列化的格式化类,所以就把里面的那个XML序列化给忽视了,刚开始一直以为跟那个SoapFormatter沾点关系的就差不多叫XML序列化了,马马虎虎马马虎虎呀。后来深入点后才发现这里其实很不然,XmlSerializer这个类里学问大了,XML序列化原来有它的特殊性。
首先当然是它们代码写法就不太一样,有点细微的差别。
SOAP序列化:
FileStream fs = new FileStream("Serial.soap",FileMode.Create,FileAccess.Write);
SoapFormatter Formatter = new SoapFormatter();
Formatter.Serialize(fs,obj);
二进制序列化跟上述的差不多,都是实现了IFormatter这个接口的类来进化序列化工作的。而XML序列化则跟IFormatter这个接口毫无关系。
FileStream fs = new FileStream("Serial.xml",FileMode.Create,FileAccess.Write);
XmlSerializer xs = new XmlSerializer(typeof(Myclass));
xs.Serialize(fs,obj);
就这么一个毫无关系令双方的序列化过程是极其的不同。
前者(指二进制与SOAP)的序列化在内部就是搞了个Writer类似的东西对目标的FileStream写呀写呀写就结束了,当然序列化的过程是极其复杂的,要对一个类里会出现的各种各样情况都要顾及喽,但原理就是那样了――根据某些条件写文件嘛。以前看CodeDom时也看到过类似的场景,只不过CodeDom里那个CodeCompileUnit类要简单得多,那里只是一层层的对CodeCompileUnit这个类一边剥一边写下来就行了。而基本上SOAP序列化还那么有章可循点,而二进制序列化就有点天差地别了,极其之繁琐呀,这里不去深究了。
按理说,不就是序列化吗?就把一个比较抽象的类用一个可以看起来具体点的文件表示出来,而XML序列化不就是把一个类表示成XML文件(不是SOAP那种的XML文件)吗,这跟SOAP序列化应该是大同小异的。道理上是这样讲,XML序列化到最后当然也是要写文件的,在.net里写文件如何写当然也没什么大的奥秘,也是如上法所炮制,那XML序列化特殊在哪里呢?
深入地分析一下会发现(受montaque启发),在XML序列化的时候会生成一个临时程序集,序列化的时候会通过Invoke那个临时程序集里的某个方法来执行序列化的动作。
在上面XmlSerializer这个类的初始化的时候有如下的代码:
if (this.tempAssembly == null) {
XmlReflectionImporter importer1 = new XmlReflectionImporter(defaultNamespace);
this.tempAssembly = XmlSerializer.GenerateTempAssembly(importer1.ImportTypeMapping(type));
XmlSerializer.cache.Add(defaultNamespace, type, this.tempAssembly);
}
会根据需要序列化的类型生成临时程序集,这里会用CodeDom来动态生成代码,然后编译之,这一步在 XmlSerializer xs = new XmlSerializer(typeof(Myclass));这一句执行后就完成了。在Debug程序的时候把断点设在此句之后,程序运行到断点处会在Output窗口看到如下类似的输出:
'XmlSerialization.exe': Loaded 'vw74dxbv', No symbols loaded.
Loaded后引号里那个乱七八糟的东西应该就是生成的临时程序集了。
它到底生成什么样的程序集进行序列化的呢?后来发现原来配置一下就可以把那个临时代码文件拿到的。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4" />
</switches>
</system.diagnostics>
</configuration>
.config文件如上所设就可以让序列化过程是产生的临时文件在程序运行结束后还在你的硬盘上保留着了。
这下好,运行一下程序,然后到Temp目录下找一个看起来名字比较奇怪的cs文件打开看一下(同目录下还有dll,pdb文件等等)。发现里面有一个public class XmlSerializationWriter1 : System.Xml.Serialization.XmlSerializationWriter,里面就是几个Write开头的方法(当然同时里面还有一个用于反序列化的XmlSerializationReader1,有一堆Read开头的方法),在这些方法里面可以看到很多名字比较熟悉的用来写XML文件的方法调用,比如WriteStartDocument,WriteStartElement等等,当然这些方法是XmlSerialization这个类里的,最后调用的就是XmlTextWriter里的同名方法。
这一下殊途同归了。
关于XML序列化,还有很多很多,比如可以序列化的项,序列化的安全性,还有关于Namespaces、Attributes对序列化的影响等等,SDK上讲了很多,都看不过来,太强大。
由此在XML序列化过程中也很容易出各种各样的问题,在MSDN上有一篇文章XmlSerializer 常见问题疑难解答谈到很多问题,居然还可以调试序列化代码。