.net自己除了支持SOAP、XML、二進制等序列化和反序列化,後來也加入了對JSON的序列化的支持。然而,在實際開發中,經常會遇到結構不肯定的JSON對象,這些對象多是其餘代碼動態生成的,你事先沒法估計它的結構,甚至它的字段名字是動態改變的。json
這種狀況下,咱們很難用一個固定的類來進行反序列化,後來我嘗試過從DynamicObject類派生出一個自定義的動態類型,但願經過這種方法可以將動態生成的JSON讀出來,但結果依舊不可;後來我又實現了ISerializable接口,想着自行去控制一下數據的讀取,但仍然未果。框架
最終我總結出來,只有下面這個方法比較省事,而且能夠作到將動態的JSON進行反序列化。this
作ASP.NET開發的朋友應該會熟悉一個類——位於System.Web.Script.Serialization命名空間下的JavaScriptSerializer類。由於這個類是爲Web開發服務的,其實能夠用於整個.net框架,即你在WinForm、WPF等程序中依舊能夠用。這個類的做用是將指定的JSON字符串進行序列化和反序列化,參與操做的類型能夠是固定的,若是JSON是固定結構的,這樣就可行。而對於結構不固定的JSON,這個類能夠以字典的形式進行操做,即調用DeserializeObject方法後會返回一個Object類型的對象,實際上這個對象是實現了IDictionary<string, object>接口的,這樣一來,反序列化的結果就能夠做爲字典來操做。若是JSON裏面有嵌套的對象,則返回的字典對象中會嵌套着字典對象。spa
因而,我就寫了這麼一個類:.net
public sealed class JsonObjectReader { private string innerJson = null; public JsonObjectReader(string json) { innerJson = json; } public dynamic GetObject() { dynamic d = new ExpandoObject(); // 將JSON字符串反序列化 JavaScriptSerializer s = new JavaScriptSerializer(); object resobj = s.DeserializeObject(this.innerJson); // 拷貝數據 IDictionary<string, object> dic = (IDictionary<string, object>)resobj; IDictionary<string, object> dicdyn = (IDictionary<string, object>)d; foreach (var item in dic) { dicdyn.Add(item.Key, item.Value); } return d; } }
有人會問我,GetObject方法爲何要返回動態類型?是爲了方便操做,ExpandoObject是一種簡單易用而且現成的動態類型,在C#中聲明變量時應用上dynamic關鍵字,告訴編譯器這傢伙是動態類型,在編譯檢查時能夠「網開一面」。並且,我發現ExpandoObject類是顯式實現了IDictionary<string, object>接口的,說明你還能夠把它強制轉換爲字典數據來操做。code
這種作法一箭雙鵰,若是方便使用,就當成動態對象來訪問,在不方便使用時,也能夠看成字典數據來用。orm
下面舉一個不方便使用動態訪問的例子:對象
string json = "{" + "\"0592\" : \"廈門市\"," + "\"0351\" : \"太原市\"," + "\"0411\" : \"大連市\"," + "\"0459\" : \"大慶市\"" + "}"; JsonObjectReader rd = new JsonObjectReader(json); dynamic res = rd.GetObject(); IDictionary<string, object> d = (IDictionary<string, object>)res; foreach (var item in d) { Console.WriteLine($"{item.Key} = {item.Value}"); }
大夥會發現,這個JSON你是很難用常規方法進行反序列化的,由於它的字段是城市的區號,是不固定的,在聲明類時你沒法事先肯定類的屬性或字段成員。同時你也發現,字段名是數字的,就算以動態對象獲得結果,你也不能以obj.0459這樣的語法來訪問,由於標識符是不能由數字開頭的。這種狀況下不能用動態對象來訪問,但能夠把它轉換爲字典對象來處理。blog
獲得結果以下圖。接口
可是,下面這種用法,由於JSON的字段名不是數字開頭,因此可以以動態對象的方式訪問。
json = "{\"Name\":\"小明\", \"Age\":25, \"Email\":\"abcd@dog.cc\"}"; JsonObjectReader rd2 = new JsonObjectReader(json); dynamic res2 = rd2.GetObject(); Console.WriteLine($"姓名:{res2.Name}"); Console.WriteLine($"年齡:{res2.Age}"); Console.WriteLine($"電郵:{res2.Email}");
由於Name、Age、Email這些字段不是數字開頭,符號標識符的規範要求,因此後面能夠用res2.Name的方式來訪問,就像訪問普通對象實例同樣。
獲得的結果以下圖。
最後要說明一下的是,這種方法只用於.NET框架的應用程序,如ASP.NET、WPF等。若是是Windows Store App的話,可使用RT API中的JSON相關的類來處理,這些類都位於Windows.Data.Json命名空間。