在某些情況下,我們還是需要使用JavaScriptSerializer類的方法來操作一個類型,例如使用JavaScriptConverter來自定義特定類的序列化或者反序列化,就需要使用JavaScriptSerializer類的方法,因此我們這次就詳細看一下這個類的能力。
在JavaScriptSerializer中,我們可以看到下面可以使用的方法或者構造函數,它們都是實例方法:
Icon | Member | Description |
---|---|---|
JavaScriptSerializer() | 構造函數,用於創建一個新的JavaScriptSerializer對象,不指定JavaScriptTypeResolver | |
JavaScriptSerializer(JavaScriptTypeResolver) | 構造函數,用於創建一個新的JavaScriptSerializer對象,並使用指定的JavaScriptTypeResolver來映射特定類型與標識字符串。 | |
ConvertToType<T>(Object) | 將給定對象轉化成類型T。 | |
Deserialize<T>(String) | 將JSON字符串轉化爲類型T。 | |
DeserializeObject(String) | 將JSON字符串轉化爲一個對象。 | |
MaxJsonLength | 獲取或者設置序列化時能夠接受的JSON字符串的最大長度。 | |
RecursionLimit | 獲取或者設置在反序列化JSON字符串時遞歸的最大深度。 | |
RegisterConverters(IEnumerable<JavaScriptConverter>) | 註冊序列化過程中使用的JavaScriptConveter對象。 | |
Serialize(Object) | 將一個對象序列化成JSON字符串。 | |
Serialize(Object, StringBuilder) | 將一個對象序列化到一個StringBuilder中。 |
在這裏我們主要來看一下ConvertToType,Deserialize,DeserializeObject和Serialize的兩個重載方法。
1、ConvertToType<T>(Object)
ConvertToType<T>(Object)方法的作用是將一個Object對象轉換爲指定的對象T。這個Object對象主要的轉換,在其內部是直接調用了ObjectConverter.ConvertObjectToType(Object o, Type type, JavaScriptSerializer serializer)方法實現。ObjectConverter.ConvertObjectToType方法主要邏輯依次如下:
- 如果參數o爲null,並且type爲可以null的類型(例如引用類型,和.NET 2.0中的Nullable類型),則直接返回null。如果type不是可以爲null的類型,但是type是char類型,那麼返回'/0'。如果參數o不爲null,則將繼續下面的邏輯。
- 如果參數o是IDictionary<string, object>類型,則調用內部的ObjectConverter.ConvertDictionaryToObject方法將一個Dictionary<string, object>轉換爲type類型對象。
- 如果參數o是IList類型,則會調用內部的ObjectConverter.ConvertListToObject方法將IList類型轉換爲type類型對象。
- 如果type爲null,或者參數o已經是type對象了,那麼直接返回對象o。
- 使用TypeDescriptor.GetConverter方法獲得type對應的TypeConverter,如果該TypeConverter能夠轉換o則轉換並返回,否則會使用參數o的TypeConverter先將o轉換爲字符串(使用ConvertToInvariantString方法),再使用type對應的TypeConverter將該字符串轉換爲type類型(使用ConvertFromInvariantString方法)並返回。如果o的TypeConverter無法將o轉換爲字符串,或者type對應的TypeConverter無法將一個字符串轉換爲type類型,則只能檢查o類型是否能夠直接賦值給type類型。如果這也不行,那麼只能拋出異常了。
在最後一步的“複雜”邏輯中,似乎能夠使用提供TypeConverter來轉換對象,以此自定義序列化與反序列化能力(事實上,如果單獨使用這個方法時您的確可以這麼做),但是事實上在實際使用中作用並不大。因爲序列化與反序列化能力主要是應用在Web Service方法訪問上的,而在這裏的反序列化過程中很難使這段邏輯“遭遇”特殊的對象(雖然我們能夠通過自定義JavaScriptConvetor來“遭遇”這種情況)。在這裏,使用TypeConverter是爲了轉換一些“基礎對象”,例如Int32,Double等。另外需要注意的是,我們不能使用ConvertToType方法直接轉換客戶端序列化的日期對象,因爲日期在客戶端會被序列化成“"@23552233@"”傳遞過來,在反序列化時需要做特殊處理。
將IDictionary<string, object>和IList轉化成特定對象的邏輯比較重要,尤其是前者。我們先來看一下它的主要邏輯吧:
- 如果在這個字典中存在“__type”對應的字符串(如果不是字符串,會將其轉換爲字符串,但是這種情況毫無意義),則會使用serializer中使用的JavaScriptTypeResolver,以“__type”的值作爲類型的標示,以獲得真正需要轉化爲的目標類型。這時,目標對象可能已經是新的類型了,我們稱之爲realType。
- 如果realType類型在serializer中存在對應的JavaScriptConverter,則使用特定的Converter反序列化對象,並返回。
- 最後,如果原始的type爲字典或泛型字典,則會將這個原始IDictionary<string, object>轉換爲type對象。否則就會將構造一個realType類型的對象(需要注意的是這個類型必須有無參數的構造函數),然後會通過反射機制爲public的屬性或者變量(屬性優先)。在這裏,如果原始IDictionary<string, object>的key相對於目標類型的屬性和變量相比有多餘,也不會拋出異常。很自然,在將原始IDictionary<string, object>的value轉換爲目標字典中的value類型,或者目標類型的屬性和變量的類型時,會遞歸調用ObjectConverter.ConvertObjectToType方法。
相對來說,IList到Object的轉換就比較簡單了,它能夠支持的對象類型有Array,ArrayList,List,List<T>和其餘實現IList的類型。注意能夠在服務器端反序列化的類型都必須有無參數的構造函數,在轉換時依舊會遞歸調用ObjectConverter.ConvertObjectToType方法。
2、Deserialize<T>(String)
該方法的作用是將一個JSON字符串轉化爲類型T。
該方法的第一步,是首先將該字符串轉化成爲一箇中間類型。這個類型可能是個基礎類型(Int32,Double,DateTime等),或者IDictionary<string, object>與IList的互相嵌套(這就是JSON字符串的表示形式,一般來說,最終目標也是基礎類型)。但是需要注意是,在將一個“{...}”形式的字符串片斷轉換爲IDictionary<string, object>之後,如果發現該字典中有關於“__type”的定義,就會調用ObjectConverter.ConvertObjectToType方法立即將其轉換爲__type表示的類型,在這個過程中會將調用Deserialize<T>(String)方法的JavaScriptSerializer對象在各個操作中進行傳遞,因此起初在那個JavaScriptSerializer對象中定義的JavaScriptTypeResolver和JavaScriptConverter都會產生效果。
該方法的第二步,就是將第一步所得到的結果,使用ObjectConverter.ConvertObjectToType方法將其轉換爲目標T了。可以發現,由於JSON字符串中“__type”的作用,還是能夠在之前描述過的ObjectConverter.ConvertObjectToType邏輯的第5步中,使TypeConverter起到所需的效果的。如果合理使用,就能夠很方便的進行開發。
3、DeserializeObject(String)
該方法可以說是Deserialize<T>(String)方法的一小部分,它也分作兩步進行。其中第一步和Deserialize<T>(String)方法第一步作用完全一樣,而第二步也是使用了ObjectConverter.ConvertObjectToType方法進行轉換。由於和那個方法相比沒有指定目標對象T,因此傳遞給ObjectConverter.ConvertObjectToType方法的第二個參數則爲null,也就是說,如果進行到IDictionary<string, object>到Object的轉換,如果沒有指定“__type”,它將保持不變。至於“[...]”類型的JSON字符串,如果沒有指定type,則會默認轉換爲一個Object數組。
4、Serialize的兩個重載方法
這兩個方法的作用是使用Object轉換爲JSON字符串。Serialize(Object)方法會構造一個StringBuilder,再調用Serialize(Object, StringBuilder)方法得到結果,因此我們將目光對準後者。
其實Serialize方法的邏輯相對於Deserialize方法來說簡單了不少,畢竟拼接字符串的工作一般總是比解析字符串的工作要容易。Serialize方法本身也是個遞歸方法,會遞歸地序列化一個對象的public屬性和變量,因此在序列化一個複雜對象時往往會出現“循環引用”的狀況,這時候就就會拋出異常。這時候JavaScriptConverter就起到其作用了,在序列化某個類型時,會查找serializer中是否有其對應的JavaScriptConveter,如果有的話,則會通過這個JavaScriptConverter得到一個IDictionary<string, object>,然後再爲這個字典添加“__type”的值,最後再將這個字典對象序列化輸出。需要注意的是,得到“__type”值的方式是通過JavaScriptConverter中的JavaScriptTypeResolver來得到類型的標識字符串。這個邏輯和反序列化操作正好相反。
這些就是ASP.NET AJAX服務器端提供的序列化與反序列化的能力。而且事實上它提供的擴展能力往往也已經足夠了,這點着實爲我們省去了許許多多的麻煩。
原文地址:http://www.cnblogs.com/JeffreyZhao/archive/2006/11/10/Inside_Atlas_Series__Investigate_the_Serialization_and_Deserialization_Ability_2.html