深入Atlas系列:Web Sevices Access in Atlas示例(2) - 自定义JavaScriptConverter处理循环引用对象

简介:
中 我举了一个简单例子,说明了存在循环引用的对象在序列化时,服务器端和客户端都会出现异常。在这篇文章里,我将通过一个示例来说明如何通过自定义 JavaScriptConverter来处理存在循环引用的对象。这个示例所用的方式也基本上可以在处理复杂对象时使用,因为复杂对象最重要的一点就是 存在复杂引用,其余的特点,估计也就是成员较多了。

  同样,我们先定义存在循环引用的类。他们依旧是Boy和Girl,能够互相引用。代码如下:
ContractedBlock.gif  Boy类与Girl类代码

  然后,我们就一一发现问题,并解决它:

一、解决服务器端序列化问题:

  我们先写需要使用的Web Services方法:
ContractedBlock.gif  GetBoyWithGirlfriend方法

  很显然,它们存在循环引用,如果使用了Atlas内部的JSON序列化方法的话,就会抛出异常。那么我们该怎么解决这个问题呢?方法就是自定 义一个JavaScriptConvert。在这里,我们写一个BoyConverter,首先重载它的SupportedTypes属性,表明这个 Converter所支持的类型。
ContractedBlock.gif  SupportedTypes属性代码

  在之前文章中我们分析的代码可知,这个属性只有在初始化的Converter映射的时候被调用一次,所以我们能够直接new一个数组并返回,不会有任何的性能损失。

   接下来我们要做的事情就是重载JavaScriptConverter的Serialize方法了,它接受一个object对象作为参数,输出序列化结 果。由于我们需要序列化后的对象也保持着循环引用,因此完全使用JSON方式是无法做到的,那么我们该怎么做呢?我们知道,在客户端反序列化服务器端传来 的结果,使用的就是简单的“eval("(" + body + ")")”这样的方法,由于这一点,我们来看一下下面的代码被“反序列化”时会得到什么结果呢?  
function (){  return   100 ; }.call()

  得到的结果就是100!在这里,其实我们定义了一个函数,并过它的call方法进行了调用。在函数体中,我们可以写任意的代码,任意多的语句。这样,我们的做法就显而易见了。代码如下:
ContractedBlock.gif  Serialize方法

  首先判断对象o是不是Boy类型,如果不是,那么抛出异常。然后就通过拼接字符串输出。事实上,对于一个良好实现的Serialize方法, 它需要判断传入的Boy对象的Girlfriend属性是否为null等信息,然后输出不同的结果。在这里,仅仅是一个演示,我就没有过多追究了。:)

  我们的Serialize方法写完了,然后就要使用它了。请注意,为了使用它,我们需要将它添加到Web.config文件的相应节点中,如下:
< microsoft .web >
    
< converters >
        ……
        
< add  type ="Jeffz.JavaScriptConvertDemo.BoyConverter"   />
    
</ converters >
    ……
</ microsoft.web >

  于是我们就来看一下它的使用代码,首先是HTML
ContractedBlock.gif  HTML代码

  点击“Get!”按钮将会调用Web Services方法,并将结果显示在“GetBoyWithGirlfriend”这个DIV中。所需的Javascript如下:
ContractedBlock.gif  getBoyWithGirlfriend函数代码

  代码非常简单,就不多说了,如果得到了正确结果,那么会在DIV中显示“You did a good job!”等字样,我们直接来看一下使用效果吧:

  打开之后:
JSConverter_1.jpg

  在文本框内填上信息,并点击“Get!”按钮。得到正确结果了:
JSConverter_2.jpg

  我们看一下Fiddler获得的信息:
JSConverter_3.jpg

  很明显,我们自定义的BoyConverter生效了。:)



二、解决客户端序列化和服务器端反序列化的问题:

  再写一个Web Services方法,接收一个Boy类型的对象作为参数:
ContractedBlock.gif  SetBoyWithGirlfriend方法代码

  这次我们先来看HTML代码,和前一个例子的代码非常相似:
ContractedBlock.gif  HTML代码

  在客户端,如果构造了循环引用的对象,如果使用了默认的序列化方法,将会无限递归最终Stack Overflow。在《 深入Atlas系列:Web Sevices Access in Atlas(4) - 对于复杂数据类型的支持(上) 》中,我们看出我们可以为对象附加一个serialize方法来自定义序列化方式。那么我们就来看一下这个方法的代码:
ContractedBlock.gif  serialize方法代码

  我们最终还是会使用Sys.Serialization.JSON.serialize方法来序列化一个客户端对象,不过在这之前需要进行处 理。首先delete该对象上的serialize方法,否则调用Sys.Serialization.JSON.serialize方法时又将进入当前 的serialize函数。接下来的delete操作删除循环引用,最后才使用Sys.Serialization.JSON.serialize方法序 列化该对象。注意,在得到了序列化结果之后,需要this对象的状态恢复,例如serialize函数和循环引用。

  然后,在服务器端,我们在BoyConverter内重载Desirialize方法,目的是得到反序列化后的Boy对象。代码如下:
ContractedBlock.gif  Deserialize方法代码

  首先判断t的类型是不是Boy,然后调用基类的Deserialize方法进行反序列化。这个方法会调用Atlas内部的反序列化方法,而且 避开对于某个反序列化类型寻找相应Converter的逻辑,否则又会出现了无限递归,最终抛出StackOverflowException。最后,当 然就是恢复boy对象与boy.Girlfriend之间的循环引用了。

  我们来看一下客户端的Javascript代码:
ContractedBlock.gif  setBoyWithGirlfriend方法

  越来越感受到Function.createDelegete方法(第5行)的重要性。如果没有它,很多功能的实现都会麻烦许多。

  我们来看一下使用效果。首先,打开页面:
JSConverter_4.jpg

  在文本框内填写信息,并点击“Set!”按钮:
JSConverter_5.jpg

  说明客户端的序列化操作和反序列化操作都成功了!


三、实现更良好的客户端序列化和服务器端反序列化的代码:

  事实上,我们的序列化代码实现还不够良好。例如,如果boy.Girlfriend.Boyfriend不是boy怎么办呢(……)?因此我们不能在客户端和服务器端简单地处理序列化和反序列化操作。我们需要做一个“标记”。

  我们就在Girl对象中作标记吧,将其代码修改如下:
ContractedBlock.gif  Girl类修改后代码

  需要注意的是__HasClientBoyfriend变量的作用只是为了标记,在序列化Girl类型对象时不应该输出,所以我们使用XmlIgnoreAttribute来禁止其被序列化。

  我们在客户端的serialize方法自然也需要有些修改:
ContractedBlock.gif  serialize方法修改后代码

  在序列化的时候判断了是否的确有循环引用,如果有的话,则在this.Girlfriend对象上做一个__HasClientBoyfriend的标记。

  最后需要修改的就是BoyConverter的Deserialize方法了:
ContractedBlock.gif  修改后Deserialize方法代码

  现在,只有当boy.Girlfriend的__HasClientBoyfriend标记为true时才重新建立循环引用。如果仅仅是一个 普通的boy对象,则不会有多余的操作。如果有朋友感兴趣的话,可以使用Fiddler查看Girl类型的序列化结果,可以发现 __HasClientBoyfriend没有被序列化输出,这就是XmlIgnoreAttribute的效果。

  事实上,上面的代码多少还有点小问题,不过个人认为,作为一个示例来说,这已经足够了。


   在这篇文章中,我演示了JavaScriptConverter的基本使用,但是它的作用还不止这些。不知道大家有没有发现,在上面的示例中如果需要在 客户端作序列化操作,则需要在客户端与服务器端同时进行代码维护。显然这种做法非常不利于分发和部署组件。在今后的示例中,我将配合Atlas为Web Service方法建立Proxy的讲解,演示如何使用JavaScriptConverter来将所有的代码都在服务器端进行维护。不过那会涉及到目前 所有的文章,另外还包括以后会讲到的Atlas为Web Services方法建立Proxy方面的内容。结合Atlas的方方面面,能使我们的开发更为成熟。  


  点击这里下载示例源文件。


本文转自 jeffz 51CTO博客,原文链接:xxxhttp://blog.51cto.com/jeffz/60798xxx,如需转载请自行联系原作者

相关文章
|
5月前
|
应用服务中间件
idea 调试报错 Illegal access: this web application instance has been stopped already.
idea 调试报错 Illegal access: this web application instance has been stopped already.
78 0
|
2月前
|
SQL 开发框架 .NET
ASP.NET Web——GridView完整增删改查示例(全篇幅包含sql脚本)大二结业考试必备技能
ASP.NET Web——GridView完整增删改查示例(全篇幅包含sql脚本)大二结业考试必备技能
33 0
|
4月前
|
应用服务中间件 数据安全/隐私保护
Tomcat【部署 02】Web端403 Access Denied You are not authorized to view this page解决方法(Tomcat 10.2.12 版本)
Tomcat【部署 02】Web端403 Access Denied You are not authorized to view this page解决方法(Tomcat 10.2.12 版本)
127 0
|
5月前
|
Web App开发 存储 安全
大师学SwiftUI第17章Part1 - Web内容访问及自定义Safari视图控制器
App可以让用户访问网页,但实现的方式有不止一种。我们可以让用户通过链接在浏览器中打开文档、在应用界面中内嵌一个预定义的浏览器或是在后台下载并处理数据。
46 0
|
8月前
|
Web App开发 JavaScript 前端开发
Web组件规范和自定义元素
Web组件规范和自定义元素
|
9月前
|
搜索推荐 JavaScript 数据可视化
数据可视化大屏高德地图javascript webAPI开发的智慧治安物联网管理系统实战解析(web GIS、3D视图、个性化地图、标注、涟漪动画、自定义弹窗、3D控件)
数据可视化大屏高德地图javascript webAPI开发的智慧治安物联网管理系统实战解析(web GIS、3D视图、个性化地图、标注、涟漪动画、自定义弹窗、3D控件)
383 0
|
9月前
|
JSON 前端开发 API
layui框架实战案例(8):web图片裁切插件croppers.js组件实现上传图片的自定义截取(含php后端)
layui框架实战案例(8):web图片裁切插件croppers.js组件实现上传图片的自定义截取(含php后端)
386 0
|
10月前
|
安全 Java 编译器
【Java Web编程 八】深入理解Servlet常用对象
【Java Web编程 八】深入理解Servlet常用对象
96 1
|
10月前
|
JavaScript 前端开发 算法
web前端面试高频考点——Vue组件间的通信及高级特性(多种组件间的通信、自定义v-model、nextTick、插槽)
web前端面试高频考点——Vue组件间的通信及高级特性(多种组件间的通信、自定义v-model、nextTick、插槽)
112 0
|
10月前
|
缓存 NoSQL 前端开发
【Go】基于 Gin 从0到1搭建 Web 管理后台系统后端服务(三)路由、自定义校验器和 Redis
【Go】基于 Gin 从0到1搭建 Web 管理后台系统后端服务(三)路由、自定义校验器和 Redis