.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命名空间。