【IT168技术文档】
我们既然需要直接得到一个客户端的具体类型,也就是说,在得到JSON表示的对象之后,还必须做一些额外的事情。而这样的事情应该是服务器端控制的,也就是说在序列化的结果之后,应该将额外的工作“一并输出”。不过可惜的是,在客户端只能输出普通的数据类型,而不能输出“函数”。那么该怎么办呢?
不过还好,虽然不能输出函数,但是在脚本代码中,函数也是通过字符串的形式表示的,那么我们就用字符串来表示提供“额外工作”的函数吧。于是在这里,我设计了如下协议:
1. 在序列化输出时,如果需要有额外的工作,在对象中以字符串输出一个“__getRealObject”函数,它的形式是 “function(o){ ... }”,其中参数o就是“__getRealObject”函数所在的对象,而“__getRealObject”函数的返回值则会将该对象替换掉。
2. 扩展ASP.NET AJAX客户端访问服务器的基础结构,以利用对象输出的“__getRealObject”。
可以发现,我们在“__getRealObject”函数中其实可以定义“任意工作”,这个作用是非常大的,比如我们能够在客户端构造一个有相互引用的复杂对象。那么我们就开始实现吧:
为DataTable定义JavaScriptConverter并使用:
可以看到,我们定义的 DataTableConverter继承了Feature-add(即Beta1的Value-add)程序集中的 Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter,并覆盖了Serialize方法。可以看到,我们在获得了原来的结果之后,首先改变“dataArray”的值,这个目的是修改原来 DataTableConverter的bug。然后再增加一个“__getRealObject”,添加一个函数,这些还是相当简单的。namespace Jeffz { public class DataTableConverter : Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter { public override IDictionary<string, object> Serialize(object obj, Microsoft.Web.Script.Serialization.JavaScriptSerializer serializer) { IDictionary<string, object> result = base.Serialize(obj, serializer); string dataArray = result["dataArray"].ToString(); if (dataArray[dataArray.Length - 1] == ')') { // 顺便fix掉bug result["dataArray"] = dataArray.Substring(0, dataArray.Length - 1); } result["__getRealObject"] = "function(o) { return new Sys.Preview.Data.DataTable(o.columns, eval(o.dataArray)); }"; return result; } } }
然后我们只需修改web.config的配置,让它使用我们的DataTableConverter。如下:
扩展客户端访问服务器基础结构:<jsonSerialization maxJsonLength="500000000"> <converters> <add name="DataTableConverter" type="Jeffz.DataTableConverter"/> </converters> </jsonSerialization>
“官方”的扩展方式是自定义WebRequestExecutor,但是在这里我使用了一个另一个方法。那么直接来看代码吧:
Sys.Net.WebRequestExecutor.prototype._get_object = Sys.Net.WebRequestExecutor.prototype.get_object; Sys.Net.WebRequestExecutor.prototype._getRealObject = function(obj) { if (obj && typeof(obj) == 'object' && !Array.isInstanceOfType(obj) && !Date.isInstanceOfType(obj)) { for (m in obj) { var value = obj[m]; obj[m] = this._getRealObject(value); } var strMethod = obj["__getRealObject"]; if (strMethod) { delete obj.__getRealObject; eval("var method = " + strMethod); return method(obj); } } return obj; } Sys.Net.WebRequestExecutor.prototype.get_object = function() { var obj = this._get_object(); return this._getRealObject(obj); } Sys.Application.notifyScriptLoaded();