zoukankan      html  css  js  c++  java
  • 【.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命名空间

    示例代码下载

  • 相关阅读:
    设计模式
    jQuery回到顶部插件jQuery GoUp
    CentOS7+Tomcat 生产系统部署
    iOS 时间戳转换为时间
    iOS开发系列--Swift 3.0
    IOS
    iOS之宏定义#define
    #define和预编译指令
    iOS宏定义的使用与规范
    ios十进制、十六进制字符串,byte,data等之间的转换
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/4775297.html
Copyright © 2011-2022 走看看