Ajax最强悍的功能莫过于服务器和客户端之间的异步交互,他们在交互的时候不是通过soap协议等,而是通过回调函数,以Json的格式传送数据。
由于Json格式的限制,在很多情况下,稍微复杂一些的数据往往会引起循环引用的错误,至于什么是循环引用?什么时候会发生循环引用?这里不再赘述,详见上篇博客。
都在强调Ajax的强大,那么就举这么一个例子:自定义一个复杂类型的数据,一个男孩类和一个女孩类互为对象,在客户端访问的时候就会出现循环引用,初学的我们或许会感叹:Ajax也不过如此嘛!其实不然,Ajax早已做好了解决这个问题的方法,那就是——序列化和反序列化!个人觉得它就好比一副穿在Json数据格式上的漂亮嫁衣,让程序猿们对Ajax青睐有嘉……
上一篇博客中总结了微软已经为常用的dt家族(datatable、dataset、datarow)的序列化和反序列化的封装;这里让我们为自定义的数据类型定制一套自己的嫁衣吧!
首先我们需要考虑这么几个问题:怎么来序列化和反序列化?什么时候需要序列化和反序列化?接下来就让每一个步代码来解释这些问题,希望看完这篇文章这几个问题可以迎刃而解。
==============================================================================================================================================================================
一、材料准备——循环引用的两个类的定义
1、在服务端App_Code目录下添加ComplexType文件夹下创建BoyAndGirl.cs类文件。
在BoyAndGirl.cs类文件中添加如下代码,定义Boy和Girl类,并指定可以相互访问。
- <span style="font-size:18px;">using System;
- using System.Data;
- using System.Configuration;
- using System.Web;
- using System.Web.Security;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using System.Web.UI.HtmlControls;
-
- namespace ComplexType
- {
-
-
- public class Boy
- {
- public string Name;
-
- public Girl GirlFriend;
- }
-
-
- public class Girl
- {
- public string Name;
-
- public Boy BoyFriend;
- }
- }</span>
2、添加BoyGirlService.asmx服务,并在服务中创建Boy类和Girl类的循环引用。
- <span style="font-size:18px;">using System;
- using System.Web;
- using System.Web.Services;
- using System.Web.Services.Protocols;
- using System.Web.Script.Services;
- using ComplexType;
- using System.Diagnostics;
-
- [WebService(Namespace = "http://tempuri.org/")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [ScriptService]
- public class BoyGirlService : System.Web.Services.WebService {
-
- [WebMethod]
-
- public Boy GetBoyWithGirlFriend()
- {
- Boy boy = new Boy();
- boy.Name = "Terry";
-
- Girl girl = new Girl();
- girl.Name = "Marry";
-
- boy.GirlFriend = girl;
- girl.BoyFriend = boy;
-
- return boy;
- }
-
- [WebMethod]
-
- public string SetBoyWithGirlFriend(Boy boy)
- {
-
-
- Debug.Assert(boy == boy.GirlFriend.BoyFriend);
-
- return String.Format(
- "It‘s {0}, his girlfriend is {1}",
- boy.Name, boy.GirlFriend.Name);
- }
-
- }</span>
现在我们已经在服务端定义了Boy类和Girl类,并在asmx文件中定义了可以让客户端Ajax的脚本调用的循环引用的方法。嫁衣的材料已经准备完毕。
二、嫁衣制作——自定义序列化和反序列化
有了App_Code下的Boy类、Girl类的定义以及asmx中的循环引用的方法GetBoyWithGirlFriend,接下来,就着手DIY我们的嫁衣吧。
在App_Code目录下添加Converter文件夹,并在该文件夹下创建BoyConverter.cs类,封装序列化的反序列化的方法。
- <span style="font-size:18px;">using System;
- using System.Data;
- using System.Configuration;
- using System.Web;
- using System.Web.Security;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using System.Web.UI.HtmlControls;
- using System.Web.Script.Serialization;
- using System.Collections.Generic;
- using ComplexType;
-
- namespace Converter
- {
-
- public class BoyConverter : JavaScriptConverter
- {
-
-
-
-
-
-
-
- public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
- {
- Boy boy = new Boy();
- boy.Name = (string)dictionary["Name"];
-
- boy.GirlFriend = serializer.ConvertToType<Girl>(dictionary["GirlFriend"]);
-
- boy.GirlFriend.BoyFriend = boy;
-
- return boy;
- }
-
-
-
-
-
-
- public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
- {
- Boy boy = (Boy)obj;
-
-
- IDictionary<string, object> result = new Dictionary<string, object>();
-
- result["Name"] = boy.Name;
-
- boy.GirlFriend.BoyFriend = null;
-
- result["GirlFriend"] = boy.GirlFriend;
-
- return result;
- }
-
- public override IEnumerable<Type> SupportedTypes
- {
- get
- {
-
-
-
- yield return typeof(Boy);
- }
- }
- }
-
- }</span>
这里突出总结一些我对Json、字典的概念的理解:
json字串是一个json对象的字串表现形式,json对象是使用字典和列表(dictionary和list)两种结构互相嵌套、引用的方法来表示的对象;对于每一个对象来说,我们都可以想象成是一个字典,由key和value组成,一个key个value组成一个item;一个json对象可以有多个item。
三、天使降临——客户端调用
这里只贴出客户端的javascript脚本:
- <span style="font-size:18px;"><script language="javascript" type="text/javascript">
- function getBoy() {
-
- BoyGirlService.GetBoyWithGirlFriend(onGetBoySucceeded, onFailed);
- }
- function onGetBoySucceeded(result) {
-
-
-
- alert(String.format(
- "It‘s {0}, his girlfriend is {1}",
- result.Name,
- result.GirlFriend.Name));
- }
-
- function onFailed(error)
- {
- alert(error.get_message());
- }
-
- function setBoy()
- {
- var boy = new Object();
- boy.Name = "Terry";
- var girl = new Object();
- girl.Name = "Mary";
- boy.GirlFriend = girl;
-
- BoyGirlService.SetBoyWithGirlFriend(boy, onSetBoySucceeded, onFailed);
- }
- function onSetBoySucceeded(result)
- {
- alert(result);
- }
- </script></span>
这样我们就自制了一套序列化和反序列化的嫁衣,来看一下运行效果:
技术思想总结——怎么来序列化和反序列化?
回顾一下序列化和反序列化的思路就是:在服务端先打乱Boy对象的GirlFriend原有的循环引用,或者说是先解开BoyFriend和GirlFriend之间的循环引用,然后重新建立起BoyFriend和Girlfriend之间的循环引用。
怎么打乱循环引用?核心的代码就是将对象属性赋值为空后,重新赋值即可:
- <span style="font-size:18px;">
- boy.GirlFriend.BoyFriend = null;
-
- result["GirlFriend"] = boy.GirlFriend;</span>
视野拓展——新知识学习: 泛化的字典数据类型和泛化的枚举的使用
在序列化的时候,使用的是IDictionary<>的泛化类型,传入一个obj对象,然后经过序列化后返回的result是一个IDictionary类型。其他类型的自定义数据类型,也可以借用该思路。
反序列化基本上是一个相反的步骤,传入IDictionary类型,返回obj对象类型。 result和obj对象都由key和value组成。
另外,如果需要我们还可以在客户端重新添加解开后的循环引用。
- <span style="font-size:18px;">
补充说明:
因为用到了泛化和序列化反序列化,需要添加相应的引用:
- using System.Web.Script.Serialization;
- using System.Collections.Generic;