深入Atlas系列:Web Sevices Access in Atlas示例(7) - 编写JavaScriptConverter处理含有循环引用的类型

  有时候在Web Service中会需要使用到比较复杂的类型,它们的特征往往都是含用循环引用,这样的对象如果交给ASP.NET AJAX中默认的序列化方式来处理则会抛出异常,大家经常遇到的“DataTable”问题正是由此引起的。关于这一点,ASP.NET AJAX自然提供了解决方法,在这里“官方”的解决方案就是JavaScriptConverter,它可以让开发人员自定义特定类型的序列化能力。

   事实上,需要自定义JavaScriptConverter的类型不止“含有循环引用”的类型,事实上,JavaScriptConverter的目标 是“ASP.NET AJAX中无法操作,或者结果不是开发人员所期望那样”的类型,这里的“操作”包括“序列化”于“反序列化”两部分。再举个例子:如果一个类型没有无参数 的构造函数,那么也需要定义JavaScriptConverter,否则ASP.NET AJAX无法对其进行反序列化操作。

  那么我们就通过一个简单的例子来看一下应该如何开发和使用JavaScriptConverter吧。


1、定义存在循环引用的类型

  首先我们定义一个Boy类和Girl类以供使用:
public   class  Boy
{
    
public   string  Name;
    
    
public  Girl GirlFriend;
}

public   class  Girl
{
    
public   string  Name;

    
public  Boy BoyFriend;
}

  很显然,如果我将它们“配成一对”,在序列化输出时就会抛出异常了。就冲着这点,我们就必须定义一个JavaScriptConverter啊,总不能拆散他俩。


2、定义JavaScriptConverter以及序列化能力

  我们下面就该开始定义JavaScriptConverter了,我们姑且将其称之为BoyConverter。首先需要告诉ASP.NET,我们这个Converter可以支持哪些类型:
public   class  BoyConverter : JavaScriptConverter
{
    
public   override  IEnumerable < Type >  SupportedTypes
    {
        
get
        {
            yield 
return   typeof (Boy);
        }
    }
    ……
}

  如果要实现一个比较良好的Serialize方法,就需要处理“有循环引用”和“没有循环引用”两种情况。幸运的是,对于Serialize方法来说,这点比较容易:
public   override  IDictionary < string object >  Serialize( object  obj, JavaScriptSerializer serializer)
{
    IDictionary
< string object >  result  =   new  Dictionary < string object > ();
    
    Boy boy 
=  (Boy)obj;
    result[
" Name " =  boy.Name;

    
//  如果有GirlFriend引用
     if  (boy.GirlFriend  !=   null )
    {
        
//  摘除循环引用
        boy.GirlFriend.BoyFriend  =   null ;
        result["GirlFriend"] = boy.GirlFriend;

        
//  在客户端再建立关联
        result[ " __getRealObject " =
            
" function(o) { o.GirlFriend.BoyFriend = o; return o; } " ;
    }

    
return  result;
}

  在这里,我们“手动”地将Boy对象转换为了一个IDictionary<string, objct>,这样就避免出现了循环引用。另外,为了在客户端直接得到一个互相引用的“Boy”和“Girl”对象,我在这里使用了我在前一片文章 中提到的扩展,具体请见《深入Atlas系列:综合示例(1) - 调用服务器端方法时直接获得客户端具体类型》。


3、自定义序列化功能使用示例

  那么我们来看一下使用示例吧,首先我们需要定义一个Web Service方法:
[WebService(Namespace  =   " [url]http://tempuri.org/[/url] " )]
[WebServiceBinding(ConformsTo 
=  WsiProfiles.BasicProfile1_1)]
[Microsoft.Web.Script.Services.ScriptService]
public   class  BoyGirlService  : System.Web.Services.WebService {

    [WebMethod]
    
public  Boy GetBoy( string  boyName,  string  girlName)
    {
        Boy boy 
=   new  Boy();
        boy.Name 
=  boyName;

        
if  ( ! String.IsNullOrEmpty(girlName))
        {
            Girl girl 
=   new  Girl();
            girl.Name 
=  girlName;
            
            girl.BoyFriend 
=  boy;
            boy.GirlFriend 
=  girl;
        }

        
return  boy;
    }    
}

  然后是页面中的HTML:
< asp:ScriptManager  runat ="server"  ID ="ScriptManager" >
    
< Scripts >
        
< asp:ScriptReference  Path ="ExecutorExtention.js"   />
    
</ Scripts >
    
< Services >
        
< asp:ServiceReference  Path ="BoyGirlService.asmx"   />
    
</ Services >
</ asp:ScriptManager >

< input  type ="button"  value ="Get Boy with Girlfreind"  onclick ="GetBoy('Tom', 'Mary');"   />
< input  type ="button"  value ="Get Boy only"  onclick ="GetBoy('Tom', null)"   />
< br  />
< div  id ="result" ></ div >

  当然还有必须的JavaScript代码:
function  GetBoy(boyName, girlName)
{
    BoyGirlService.GetBoy(boyName, girlName, onGetBoySuccess);
}

function  onGetBoySuccess(boy)
{
    
if  (boy.GirlFriend  &&  boy.GirlFriend.BoyFriend  ==  boy)
    {
        $get('result').innerHTML 
=  String.format(
            'The boy is {
0 } and his girlfriend is { 1 }.',
            boy.Name, boy.GirlFriend.Name);
    }
    
else   if  ( ! boy.GirlFriend)
    {
        $get('result').innerHTML 
=  String.format(
            '{
0 } is a boy seeking girlfriend.', boy.Name);
    }
    
else
    {
        $get('result').innerHTML 
=  ' < b > Error: </ b >  Something wrong  in  your code ! ';
    }
}

  当然,最重要的就是在web.config里指定JavaScriptConverter了:
< jsonSerialization  maxJsonLength ="500" >
    
< converters >
        
< add  name ="BoyConverter"  type ="Jeffz.BoyConverter" />
    
</ converters >
</ jsonSerialization >

  打开页面,当点击“Get Boy with GirlFriend”按钮时:


  当点击“Get Boy only”时:


  我们的这说明我们的JavaScriptConverter生效了!


4、定义反序列化能力

  当然,我们可能还需要将Boy对象用作Web Service方法的参数,因此,我们还必须定义反序列化能力。可惜的是,客户端的序列化能力不能简单地自定义,因此我们只能将boy对象在序列化之前先将其“拆开”。

  那么我们来看一下Deserialize方法吧:
public   override   object  Deserialize(IDictionary < string object >  dictionary, Type type, JavaScriptSerializer serializer)
{
    Boy boy 
=   new  Boy();
    boy.Name 
=  dictionary[ " Name " ].ToString();

    
if  (dictionary.ContainsKey( " GirlFriend " ))
    {
        boy.GirlFriend 
=  serializer.ConvertToType < Girl > (dictionary[ " GirlFriend " ]);
        boy.GirlFriend.BoyFriend 
=  boy;
    }

    
return  boy;
}

  我们在这里可以利用传入的JavaScriptSerializer来转换Girl对象,只要合理利用JavaScriptSerializer,很多时候我们只需要写一点点代码。这个方法还是非常简单的。


5、自定义反序列化功能使用示例

  首先,我们还是写一个Web Service方法:
[WebMethod]
public   string  ShowBoy(Boy boy)
{
    
if  (boy.GirlFriend  !=   null   &&  boy.GirlFriend.BoyFriend  ==  boy)
    {
        
return  String.Format(
            
" The boy is {0} and his girlfriend is {1}. " ,
            boy.Name, boy.GirlFriend.Name);
    }
    
else   if  (boy.GirlFriend  ==   null )
    {
        
return  String.Format( " {0} is a boy seeking girlfriend. " , boy.Name);
    }
    
else
    {
        
throw   new  Exception( " Something wrong in your code! " );
    }
}

  接着是HTML代码:
< input  type ="button"  value ="Show Boy with Girlfriend"  onclick ="ShowBoy('Tom', 'Mary');"   />
< input  type ="button"  value ="Show Boy only"  onclick ="ShowBoy('Tom', null);"   />

  最后是JavaScript代码:
function  ShowBoy(boyName, girlName)
{
    
var  boy  =  {Name : boyName};
    
    
if  (girlName)
    {
        boy.GirlFriend 
=  { Name : girlName };
    }
    
    BoyGirlService.ShowBoy(boy, onShowBoySuccess);
}

function  onShowBoySuccess(result)
{
    $get('result').innerHTML 
=  result;
}

  当点击两个按钮时,其效果和之前的例子完全相同,在这里就不重复展示了。


6、总结

   ASP.NET AJAX提供的自定义序列化与反序列化能力是通过JavaScriptConverter来提供的,一般来说我们能够很轻松地进行这方面的扩展。另外,合 理利用ASP.NET AJAX提供的序列化能力,能够很方便地进行开发。关于ASP.NET AJAX序列化能力的分析,请参考我之前的文章《深入Atlas系列:探究序列化与反序列化能力(上) - 客户端支持,JavaScriptTypeResolver与JavaScriptConverter》和《深入Atlas系列:探究序列化与反序列化能力(下) - JavaScriptSerializer》。



  点击这里下载示例。

你可能感兴趣的:(JavaScript,循环,休闲,Atlas)