淺析ASP.NET的TypeResolver
一、客戶端的序列化與反序列化能力
在ASP.NET AJAX中,為客戶端提供序列化能力的是Sys.Serialization.JavaScriptSerializer類的serialize靜態(tài)方法。這個方法能夠?qū)⒁粋€客戶端對象序列化成為一個JSON字符串,它的使用方法非常簡單。如下:
- var jsonStr = Sys.Serialization.JavaScriptSerializer.serialize(obj);
沒有過多可說的內(nèi)容,可能比較“有特點(diǎn)”的地方就是它對于客戶端Date對象的序列化操作。如果我們調(diào)用下面的代碼,會出現(xiàn)什么結(jié)果呢?
- var jsonStr = Sys.Serialization.JavaScriptSerializer.serialize(new Date());
得到的結(jié)果類似于是“"@1162814090119@"”,請注意兩邊還有雙引號。這個是一個ASP.NET AJAX對于Date對象比較特殊的表示方法,如果在某些時候開發(fā)人員需要自己來“拼接”字符串時,就需要注意這一點(diǎn)。
給ASP.NET AJAX客戶端帶來反序列化能力的就是Sys.Serialization.JavaScriptSerializer類的deserialize靜態(tài)方法。如下:
- var obj = Sys.Serialization.JavaScriptSerializer.deserialize(jsonStr);
它事實(shí)上只是簡單地調(diào)用了 JavaScript內(nèi)置的eval方法。當(dāng)然,既然序列化時對于Date對象有特殊的表示方法,在反序列化時,也會考慮到這一點(diǎn): Sys.Serialization.JavaScriptSerializer類的deserialize靜態(tài)方法在調(diào)用Evail之前,會把 “"@...@"”變成“new Date(...)”的形式,這就是標(biāo)準(zhǔn)的JSON字符串了。
二、JavaScriptTypeResolver與JavaScriptConverter
客戶端的序列化和反序列化非常簡單,我把它放在這里一并說明更像是為了讓內(nèi)容更加完整。而服務(wù)器端的序列化與反序列化就不是那么輕易的了,它涉及到大量的字符串操作,也涉及到一定的自定義能力。這才是這片文章想要著重說明的。
ASP.NET AJAX提供的序列化和反序列化能力都是由Microsoft.Web.Script.Serialization這個命名空間下的類完成的。不過幸運(yùn)的是,他們大都是內(nèi)部類,真正能夠給開發(fā)人員使用的只有JavaScriptSerializer類的數(shù)個方法而已。ASP.NET AJAX已經(jīng)帶給我們比較充足的序列化與反序列化的能力,我們只需要掌握它,知道它們是如何工作的,那一般也就足夠了。
不過要進(jìn)入對于這些序列化與反序列化能力的了解,首先需要了解其它的兩個類:JavaScriptTypeResolver和JavaScriptConverter。
1、JavaScriptTypeResolver
JavaScriptTypeResolver是一個抽象類,雖然是第一次在Atlas多個Release中出現(xiàn),但是它并不是一個新鮮事物。它的作用就相當(dāng)于Atlas CTP中的IJavaScriptSerializeContext接口,甚至可以說只是換了類名和方法名(事實(shí)上,從一個接口轉(zhuǎn)變?yōu)橐粋€抽象類,這個做法讓人摸不著頭腦,因?yàn)楝F(xiàn)在的抽象類也不存在任何的實(shí)現(xiàn))。這個類的作用是“將一個字符串,與一個特定的類進(jìn)行關(guān)聯(lián),使字符串成為那個特定類的一個標(biāo)識 ”。這個抽象類存在著兩個方法:
1). String ResolveTypeId(Type):得到Type對象的標(biāo)識字符串。
2). Type ResolveType(String):從字符串標(biāo)識獲取一個Type對象。
可以看出,這兩個方法是一對相反的操作。他們會分別運(yùn)用在序列化于反序列化操作之中。如果對于這個類的作用還不是非常了解的話,那么可以看一下 ASP.NET AJAX中這個抽象類的一個簡單實(shí)現(xiàn)。那就是 Microsoft.Web.Script.Serialization.SimpleTypeResolver類。它的代碼如下:
- public sealed class SimpleTypeResolver : JavaScriptTypeResolver
- {
- public override Type ResolveType(string id)
- {
- return Type.GetType(id);
- }
- public override string ResolveTypeId(Type type)
- {
- if (type == null)
- {
- throw new ArgumentNullException("type");
- }
- return type.AssemblyQualifiedName;
- }
- }
SimpleTypeResolver的作用是將一個類的Assembly Qualified Name與一個類型關(guān)聯(lián)了起來。但是個人認(rèn)為千萬不要使用這個類,如果用了這個類的話,Strong Named Assembly的信息不是都暴露出去了嗎?Version,Culture,PublicKeyToken,“一個都不能少”。
2、JavaScriptConverter
JavaScriptConverter類的作用是提供了開發(fā)人員自定義序列化與反序列化的能力,這一點(diǎn)對于操作含有循環(huán)引用的復(fù)雜對象尤其重要。在之前的文章中我分析過這個類,也有過這個類的使用示例。不過這個類在RTM Release中的功能被精簡了。它的方法和屬性被縮減成了三個:
1). IEnumerable<Type> SupportedTypes:只讀屬性,返回這個Converter所有能夠支持的類。
2). object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer):
這個方法的第一個參數(shù)是一個字典,有朋友可能會認(rèn)為這個字典和JSON字符串的表示非常的接近:由Dictionary和List嵌套而成,最底端的元素為一些基本類型對象。不過事實(shí)上不是如此。ASP.NET AJAX在反序列化一個JSON字符串時,如果出現(xiàn)了“{ "__type" : "...", ...}” 這樣的片斷時,在將其轉(zhuǎn)換為真正的JSON表示的Dictionary(只存在基本類型對象的Dictionary)之后,如果發(fā)現(xiàn)該 Dictionary存在“__type”這個Key,那么就會設(shè)法在這個時候就將它轉(zhuǎn)換為__type值表示的那個類型了。也就是說, JavaScriptConverter的Deserialize方法接受到的第一個參數(shù)字典中,也有可能已經(jīng)是一個特殊的類型了。
第二個參數(shù)為轉(zhuǎn)換的目標(biāo)類型。而第三個參數(shù),則是調(diào)用當(dāng)前Deserialize方法的JavaScriptSerializer了,我們的一些反序列化操作可以委托給它執(zhí)行,它已經(jīng)關(guān)聯(lián)好了web.config中配置的JavaScriptConverter。不過需要注意的就是,千萬要避免下一步操作又沒有改變地回到了當(dāng)前的Deserialize方法,顯然這樣會出現(xiàn)死循環(huán)。
3). IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer):這個方法的作用相對純粹一些,將obj對象轉(zhuǎn)換為一個IDictionary<string, object>對象,在這個方法將結(jié)果返回后,ASP.NET AJAX會在這個Dictionary中添加“__type”的值,這樣的話,在反序列化時也能夠使用當(dāng)前的JavaScriptConverter來進(jìn)行相反的操作。
3、使用JavaScriptTypeResolver與JavaScriptConveter
當(dāng)定義了JavaScriptTypeResolver與JavaScriptConverter后,還需要將其添加進(jìn)某個JavaScriptSerializer后才能生效。代碼大致如下:
- // 定義一個JavaScriptTypeResolver實(shí)例
- JavaScriptTypeResolver resolver = new MyTypeResolver();
- // 創(chuàng)建一個使用上面Resolver的JavaScriptSerializer
- JavaScriptSerializer serializer = new JavaScriptSerializer(resolver);
- // 創(chuàng)建一個JavaScriptConverter數(shù)組
- JavaScriptConverter[] converters = new JavaScriptConverter[] { new MyConverter() };
- // 將Converter關(guān)聯(lián)到Serializer中
- serializer.RegisterConverters(converters);
- // 使用JavaScriptSerializer進(jìn)行序列化或反序列化操作
- serializer.Serialize(...);
關(guān)于JavaScriptConverter的使用,還需要提一點(diǎn),就是在web.config文件中可以進(jìn)行一些配置。如下:
- <jsonSerialization>
- <converters>
- <add name="..." type="..." />
- ...
- </converters>
- </jsonSerialization>
需要注意的是,有些朋友認(rèn)為在 web.config里進(jìn)行了JavaScriptConverter配置后,這些Converter就會默認(rèn)被運(yùn)用在 JavaScriptSerializer的使用上。但是事實(shí)上這些配置的Converter只會被運(yùn)用在Web Service的訪問上,如果新創(chuàng)建了一個JavaScriptSerializer,則需要重新分配,才能使JavaScriptConverter生效。
【編輯推薦】