【.NET深呼吸】如何反序列化動態JSON


 

.net本身除了支持SOAP、XML、二進制等序列化和反序列化,后來也加入了對JSON的序列化的支持。然而,在實際開發中,常常會遇到結構不確定的JSON對象,這些對象可能是其他代碼動態生成的,你事先無法估計它的結構,甚至它的字段名字是動態改變的。

這種情況下,我們很難用一個固定的類來進行反序列化,后來我嘗試過從DynamicObject類派生出一個自定義的動態類型,希望通過這種方法能夠將動態生成的JSON讀出來,但結果依舊不可;后來我又實現了ISerializable接口,想着自行去控制一下數據的讀取,但仍然未果。

最終我總結出來,只有下面這個方法比較省事,並且可以做到將動態的JSON進行反序列化。

做ASP.NET開發的朋友應該會熟悉一個類——位於System.Web.Script.Serialization命名空間下的JavaScriptSerializer類。因為這個類是為Web開發服務的,其實可以用於整個.net框架,即你在WinForm、WPF等程序中依舊可以用。這個類的作用是將指定的JSON字符串進行序列化和反序列化,參與操作的類型可以是固定的,如果JSON是固定結構的,這樣就可行。而對於結構不固定的JSON,這個類可以以字典的形式進行操作,即調用DeserializeObject方法后會返回一個Object類型的對象,實際上這個對象是實現了IDictionary<string, object>接口的,這樣一來,反序列化的結果就可以作為字典來操作。如果JSON里面有嵌套的對象,則返回的字典對象中會嵌套着字典對象。

 

於是,我就寫了這么一個類:

    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>接口的,說明你還可以把它強制轉換為字典數據來操作。

這種做法一舉兩得,如果方便使用,就當成動態對象來訪問,在不方便使用時,也可以當作字典數據來用。

 

下面舉一個不方便使用動態訪問的例子:

            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這樣的語法來訪問,因為標識符是不能由數字開頭的。這種情況下不能用動態對象來訪問,但可以把它轉換為字典對象來處理。

得到結果如下圖。

 

 

但是,下面這種用法,因為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命名空間

 

示例代碼下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM