文档章节

给Ajax一个漂亮的嫁衣——Ajax系列之五(下)之序列化和反序列化

star496481
 star496481
发布于 2016/05/23 15:17
字数 2124
阅读 0
收藏 0

    Ajax最强悍的功能莫过于服务器和客户端之间的异步交互,他们在交互的时候不是通过soap协议等,而是通过回调函数,以Json的格式传送数据。

    由于Json格式的限制,在很多情况下,稍微复杂一些的数据往往会引起循环引用的错误,至于什么是循环引用?什么时候会发生循环引用?这里不再赘述,详见上篇博客

    都在强调Ajax的强大,那么就举这么一个例子:自定义一个复杂类型的数据,一个男孩类和一个女孩类互为对象,在客户端访问的时候就会出现循环引用,初学的我们或许会感叹:Ajax也不过如此嘛!其实不然,Ajax早已做好了解决这个问题的方法,那就是——序列化和反序列化!个人觉得它就好比一副穿在Json数据格式上的漂亮嫁衣,让程序猿们对Ajax青睐有嘉……

           

    上一篇博客中总结了微软已经为常用的dt家族(datatable、dataset、datarow)的序列化和反序列化的封装;这里让我们为自定义的数据类型定制一套自己的嫁衣吧!

   

   

    首先我们需要考虑这么几个问题:怎么来序列化和反序列化?什么时候需要序列化和反序列化?接下来就让每一个步代码来解释这些问题,希望看完这篇文章这几个问题可以迎刃而解。

 

==============================================================================================================================================================================

 

 

一、材料准备——循环引用的两个类的定

 

    1、在服务端App_Code目录下添加ComplexType文件夹下创建BoyAndGirl.cs类文件。

 

   

    在BoyAndGirl.cs类文件中添加如下代码,定义Boy和Girl类,并指定可以相互访问。

   

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
{
	
    // 如果一个类型没有无参数的构造函数,那么也需要定义JavaScriptConverter,否则ASP.NET AJAX无法对其进行反序列化操作。
	public class Boy
	{
		public string Name;

		public Girl  GirlFriend;
	}

    
	public class Girl
	{
		public string Name;

		public Boy BoyFriend;
	}
}


   

    2、添加BoyGirlService.asmx服务,并在服务中创建Boy类和Girl类的循环引用。

   

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)
	{
        //服务器端检测循环引用,如果不满足循环引用的话,会抛出异常
        //boy.GirlFriend.BoyFriend判断方式是不是觉得有点怪
		Debug.Assert(boy == boy.GirlFriend.BoyFriend);
        //客户端使用该方法,解析boy对象,返回相关的信息字串       
		return String.Format(
			"It's {0}, his girlfriend is {1}",
			boy.Name, boy.GirlFriend.Name);
	}
    
}


    现在我们已经在服务端定义了Boy类和Girl类,并在asmx文件中定义了可以让客户端Ajax的脚本调用的循环引用的方法。嫁衣的材料已经准备完毕。

二、嫁衣制作——自定义序列化和反序列化

 

    有了App_Code下的Boy类、Girl类的定义以及asmx中的循环引用的方法GetBoyWithGirlFriend,接下来,就着手DIY我们的嫁衣吧。

    在App_Code目录下添加Converter文件夹,并在该文件夹下创建BoyConverter.cs类,封装序列化的反序列化的方法。

   

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
	{
        /// <summary>
        /// 服务器端自定义序列化,服务器端首先要检查回传的参数是否有循环引用,与BoyGirlService.asmx服务器端的Assert方法共同保证了服务器端的完整性。
        /// </summary>
        /// <param name="dictionary"></param>
        /// <param name="type"></param>
        /// <param name="serializer"></param>
        /// <returns></returns>
		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"]);
            //重新建立起BoyFriend和Girlfriend之间的循环引用
			boy.GirlFriend.BoyFriend = boy;

			return boy;
		}
        /// <summary>
        /// 自定义数据类型的序列化,这里要在web.config中的converters节点(相当于是一个路标)中,注册定义的序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="serializer"></param>
        /// <returns></returns>
		public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
		{
			Boy boy = (Boy)obj;
            //json字串是一个json对象的字串表现形式,json对象是使用字典和列表(dictionary和list)两种结构互相嵌套、引用的方法来表示的对象;对于每一个对象来说,我们都可以想象成是一个字典,由key和value组成,一个key个value组成一个item;一个json对象可以有多个item;
            //先定义一个空的字典,返回的是一个字典,字典内部有两个项,一个是Name一个是GrilFriend,
			IDictionary<string, object> result = new Dictionary<string, object>();

			result["Name"] = boy.Name;
            //先打乱Boy对象的GirlFriend原有的循环引用,或者说是先解开BoyFriend和GirlFriend之间的循环引用
			boy.GirlFriend.BoyFriend = null;
            //因为Girl有循环引用,所以,不能使用默认的方式输出,需要先打乱Boy对象的GirlFriend原有的循环引用
			result["GirlFriend"] = boy.GirlFriend;

			return result;
		}

		public override IEnumerable<Type> SupportedTypes
		{
			get
			{
                //yield 关键字向编译器指示它所在的方法是迭代器块。
                //在迭代器块中, yield 关键字与 return 关键字结合使用,向枚举器对象提供值。
                //yield 关键字也可与 break 结合使用,表示迭代结束。
				yield return typeof(Boy);
			}
		}
	}

}

    这里突出总结一些我对Json、字典的概念的理解:

    json字串是一个json对象的字串表现形式,json对象是使用字典和列表(dictionary和list)两种结构互相嵌套、引用的方法来表示的对象;对于每一个对象来说,我们都可以想象成是一个字典,由key和value组成,一个key个value组成一个item;一个json对象可以有多个item。

三、天使降临——客户端调用

 

    这里只贴出客户端的javascript脚本:

   

<script language="javascript" type="text/javascript">
			function getBoy() {
            //调动服务端的方法,显示相关信息
				BoyGirlService.GetBoyWithGirlFriend(onGetBoySucceeded, onFailed);
			}
			function onGetBoySucceeded(result) {
			    //下面这句话是重新建立起BoyFriend和Girlfriend之间的循环引用,因为在服务器端已经解开了循环引用
				// result.GirlFriend.BoyFriend = 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>

    这样我们就自制了一套序列化和反序列化的嫁衣,来看一下运行效果:

 

   

   

    技术思想总结——怎么来序列化和反序列化?

 

    回顾一下序列化和反序列化的思路就是:在服务端先打乱Boy对象的GirlFriend原有的循环引用,或者说是先解开BoyFriend和GirlFriend之间的循环引用,然后重新建立起BoyFriend和Girlfriend之间的循环引用。

    怎么打乱循环引用?核心的代码就是将对象属性赋值为空后,重新赋值即可:

   

//先打乱Boy对象的GirlFriend原有的循环引用,或者说是先解开BoyFriend和GirlFriend之间的循环引用
			boy.GirlFriend.BoyFriend = null;
            //因为Girl有循环引用,所以,不能使用默认的方式输出,需要先打乱Boy对象的GirlFriend原有的循环引用
			result["GirlFriend"] = boy.GirlFriend;

    视野拓展——新知识学习: 泛化的字典数据类型和泛化的枚举的使用

   

    在序列化的时候,使用的是IDictionary<>的泛化类型,传入一个obj对象,然后经过序列化后返回的result是一个IDictionary类型。其他类型的自定义数据类型,也可以借用该思路。

    反序列化基本上是一个相反的步骤,传入IDictionary类型,返回obj对象类型。 result和obj对象都由key和value组成。

    另外,如果需要我们还可以在客户端重新添加解开后的循环引用。

   

//下面这句话是重新建立起BoyFriend和Girlfriend之间的循环引用,因为在服务器端已经解开了循环引用
// result.GirlFriend.BoyFriend = result;


 

补充说明:

 

    因为用到了泛化和序列化反序列化,需要添加相应的引用:

using System.Web.Script.Serialization;
using System.Collections.Generic;

     -------------------------------------------------------------------------------------------------------- 欢迎交流,如有理解错误,欢迎斧正!

本文转载自:http://blog.csdn.net/lmdcszh/article/details/7785966

star496481
粉丝 1
博文 36
码字总数 66
作品 0
广州
私信 提问
C#进阶系列——WebApi 接口参数不再困惑:传参详解

原文:C#进阶系列——WebApi 接口参数不再困惑:传参详解 前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料。如今,使用WebApi也有段时间了,今天就记录下API接口传...

杰克.陈
2017/12/19
0
0
.NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞

0X00 前言 在.NET处理 Ajax应用的时候,通常序列化功能由JavaScriptSerializer类提供,它是.NET2.0之后内部实现的序列化功能的类,位于命名空间System.Web.Script.Serialization、通过Syste...

Ivan1ee
03/22
0
0
jQuery中的Ajax----04

一、serialize()方法 做项目的过程中,表单是必不可少的,经常用来提供数据,例如注册、登录等。常规的方法是使表单提交到另一个页面,整个浏览器都会被刷新,而使用ajax技术则能够异步地提交...

指尖跳动的精灵
2015/03/09
61
0
Django 表单 (下) - 序列化操作

Django里面,当我们使用post提交form的时候,如果出现了错误,我们可以直接把错误通过obj传回给前端。如果当我们使用Ajax方式和自定义的form提交数据,这个时候就需要在后端把错误信息序列化...

beanxyz
2017/10/03
0
0
基于Json序列化和反序列化通用的封装

1. 最近项目已经上线了 ,闲暇了几天 想将JSON的序列化以及反序列化进行重新的封装一下本人定义为JSONHelp,虽然Microsoft 已经做的很好了。但是我想封装一套为自己开发的项目使用。方便后期...

zsdnr
2017/07/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

家庭作业——苗钰婷

2 编写一个程序,发出一声警报,然后打印下面的文本: Startled by the sudden sound, Sally shouted, "By the Great Pumpkin, what was that! #include<stdio.h>int main(){......

OSC_Okruuv
27分钟前
5
0
经典系统设计面试题解析:如何设计TinyURL(一)

原文链接: https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
28分钟前
3
0
2.面向对象设计原则(7条)

开闭原则 开闭原则的含义是:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。 实现方法 可以通过“抽象约束、封装变化”来实...

Eappo_Geng
30分钟前
7
0
8086汇编基础 debug P命令 一步完成loop循环

    IDE : Masm for Windows 集成实验环境 2015     OS : Windows 10 x64 typesetting : Markdown    blog : my.oschina.net/zhichengjiu    gitee : gitee.com/zhichengjiu   ......

志成就
35分钟前
5
0
使用nodeJS实现前端项目自动化之项目构建和文件合并

本文转载于:专业的前端网站➜使用nodeJS实现前端项目自动化之项目构建和文件合并 前面的话   一般地,我们使用构建工具来完成项目的自动化操作。本文主要介绍如何使用nodeJS来实现简单的项...

前端老手
48分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部