代码改变世界

深入Atlas系列:Web Sevices Access in Atlas(6) - 对于复杂数据类型的支持(下)

2006-10-17 00:56  Jeffrey Zhao  阅读(1837)  评论(5编辑  收藏  举报
  在上一篇文章中,我们提到了Atlas在将一个Dictionary转换为一个对象时,会调用对应的IJavaScriptSerializationContext对象的GetType(string)方法,以获得“真正”的目标对象类型。在Atlas对于Web Services方法的Request所引起的转换过程中,这个IJavaScriptSerializationContext对象是一个WebServiceData类的实例,它的GetType方法会在对应的那个Web Services类中寻找相关的数据类型。那么Atlas是如何寻找这些类型的呢?什么样的类型会被查找到呢?

通过查看代码,我们可以发现,WebServiceData.GetType(string)方法其实是返回它的一个叫做_clientTypesDictionary的实例变量中保存的Type。WebServiceData._clientTypesDictionary在第一被使用之前会被初始化。我们在这里略过了各个方法之间的调用,直接查看真正初始化该变量的方法:WebServiceData.ProcessClientTypes方法。代码如下:
ProcessClientTypes()方法分析

代码很容易理解,通过枚举每一个方法的每一个参数加以处理,最后还要处理返回值类型。在这里有一个相当重要的方法,就是通过ProcessClientType(Type, bool)加载可识别的参数类型,我们来看一下它的代码:
ProcessClientType(Type, bool)方法分析

标记完可以处理的类型后,还会递归的将该类型相关的所有类型做处理。例如数组的元素和属性的类型。值得注意的是,在递归处理该类型相关的类型时,只会处理具有set方法的属性,而不会处理公有实例成员变量的类型,这也就解释了在之前的文章《深入Atlas系列:Web Sevices Access in Atlas(4) - 对于复杂数据类型的支持(上)》中问题4所遇到的类型无法找到的错误。奇怪的是,在反序列化的对象之后恢复对象信息时也为实例的成员变量设值,因此个人认为,这是Atlas的一个“失误”。因此对于作为实例成员变量的类型,除非它也直接作为Web Services方法参数出现过,否则就无法被找到了。但是个人建议,在为Atlas创建Web Services方法的参数类型时,尽量完全使用属性,而不是成员变量。

不过这样的话,仅仅只有Web Services方法参数,以及相关的类型才能在反序列化的时候被使用到,这样似乎也够用了。不过Atlas的能力还远不止这些。Atlas能够将任意的类型附加在Web Services类中,使该类型能够在反序列化过程中被找到。这就是ProcessXmlIncludeAttributes方法的功能,可以看到,它在ProcessClientType()方法中被调用了。我们来看一下它的代码:
ProcessXmlIncludeAttributes方法分析

代码非常简单,就不多加解释了。可以看出,原本用来辅助XmlSerialiaer的XmlIncludeAttribute被用在这里了!这的确是一个好消息,于是我们就能通过为一个方法标记XmlIncludeAttribute来为一个Web Services添加额外支持的数据类型,例如:
[XmlInclude(typeof(AdditionalType))]

有了这个功能,就为Atlas访问Web Services方法的功能增色了不少。在以后将会有相关示例来演示XmlIncludeAttribute的有用之处。



到上面为止,从客户端序列化一个对象,到服务器端将Web Services方法的参数生成的整个过程已经讨论完了,也就是说Atlas访问Web Services方法的大部分的实现方式已经分析结束了。下面的做法自然是使用反射机制调用Web Services方法,然后再将所得的结果对象进行JSON序列化输出(Xml序列化输出非常常见,因此在这里不作讨论)。将结果序列化的功能是调用Microsoft.Web.Script.Serialization.JavaScriptObjectSerializer的静态方法Serialize(object,IJavaScriptSerializationContext)工作的,不过真正进行序列化工作的是Microsoft.Web.Script.Serialization.JavaScriptObjectSerializer类的实例方法SerializeValue。很自然,在构造JavaScriptObjectSerializer的时候传入的IJavaScriptSerializationContext对象为当前的WebServiceData。我们来看一下SerializeValue方法的实现:
SerializeValue方法分析

如果我们在Web.config里配置了与该类型对应的JavaScriptConverter(详细信息请见之前的文章《深入Atlas系列:Web Sevices Access in Atlas(5) - 对于复杂数据类型的支持(中)》),那么就会调用相应JavaScriptConverter对象的SerializeObject方法进行对象的序列化操作,这就是之前谈到的自定义对象序列化和反序列化功能的体现。在之后的文章里我将提供相应的示例来具体解释如何通过继承JavaScriptConverter来对Atlas进行扩展,在某些时候这样的扩展还是相当有必要的,特别是在使用复杂类型的时候。

在这里,我们只关注Atlas提供的内部序列化对象的方法。因此来看一下SerializeValueInternal方法的实现。代码如下:
SerializeValueInternal方法

企图对于如此清晰的代码作任何过多的解释似乎都是愚蠢的行为。需要注意的地方似乎只有在序列化中会对循环的引用进行侦测,如果发现已经被序列化过的代码又需要序列化了则会抛出InvalidOperationException。不过对于复杂对象,这个似乎是无法避免的,在之后的文章里我将提供示例来说明如何使用继承JavaScriptConveter来提供对于复杂类型的自定义序列化以及反序列化的方式,正如Atlas中为DataSet,DataTable和DataRow定义这种扩展一样。

在上面这个方法中,SerializeDictionary和SerializeEnumerable的实现都非常简单,只是将对象序列化成JSON形式的“{}”或“{}”而已。我们在这里只关注SerializeCustomObject方法的实现,代码如下:
SerializeCustomObject方法分析

通过代码可以看到,在序列化一个对象时对略过使用了XmlIgnoreAttribute标记的成员变量或属性,在开发时可以利用这一点。其余的似乎都没有什么可说的了。



就这样,Atlas调用Web Service方法对于复杂类型支持的代码就全部分析完了。代码可能虽然有点多,但是其逻辑还是相当清晰的,也提供了非常强大的自定义方式以供扩展。在这篇文章之后,我将会提供几个示例来说明“深入Atlas系列:Web Sevices Access in Atlas(6) - 对于复杂数据类型的支持”三篇文章里所提到的功能,希望对大家有用。另外,如果有朋友看了这些文章有任何感想和使用技巧的话,欢迎和我进行讨论,本人不胜感激。:)