使用JSON.NET动态解析JSON

2023/7/6 02:13:31

"使用JSON.NET动态解析JSON

在NET4.5和MVC4.0中,ASP.NET WEB API作为一个新的组件被发布,WEB API默认采用JSON.NET进行本地序列化, JSON.NET远比内置的WCF中默认的DataContractJsonSerializer和JavaScriptSerializer更加灵活,DataContractSerializer本身存在一些问题,因为它不能处理非常流行的非类型化对象和匿名类型,它的可扩展性也是非常有限的。

JSON.NET提供了强大的JSON序列化器,同时具有高级别和低级别的组件,可通过二进制序列化JSON, 支持JSON契约, 可处理XML到JSON格式的转换,提供LINQ TO JSON的访问方式,可序列化.NET任何内置的对象。在ASP.NET MVC API中,JSON.NET作为其默认的序列化器,可通过NUGET直接引入到Web API项目。

动态JSON解析

在开发中,我非常喜欢动态语言和匿名对象带来的优雅,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合、字典、动态对象和匿名对象),在这篇文章中我将通过JToken、JObject和JArray来动态解析JSON对象,使它很容易创建和检索的JSON内容而无需基础类型。

通过JObject和JArray创建JSON对象

我们先用非常简单的方法来动态创建一些JSON,可通过JToken派生的JSON.NET对象来进行,最常见的JToken派生的类是JObject和JArray。

正是因为JToken实现了IDynamicMetaProvider动态语言接口,所以可以使用dynamic关键字直观地创建动态对象,并把这个动态对象序列化为JSON字符串。 我们通过JArray和JObject来创建一个音乐专辑结构的一个示例:

[<span style=color: #2b91af;>TestMethod] <span style=color: blue;>public void JObjectOutputTest() { <span style=color: green;>// strong type instance <span style=color: blue;>var jsonObject = <span style=color: blue;>new <span style=color: #2b91af;>JObject();

    <span style=""color: green;"">// you can explicitly add values here
    </span>jsonObject.Add(<span style=""color: #a31515;"">""Entered""</span>, <span style=""color: #2b91af;"">DateTime</span>.Now);

    <span style=""color: green;"">// dynamic expando instance you can add properties to
    </span><span style=""color: blue;"">dynamic </span>album = jsonObject;

    album.AlbumName = <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>;
    album.Artist = <span style=""color: #a31515;"">""AC/DC""</span>;
    album.YearReleased = 1976;

    album.Songs = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">JArray</span>() <span style=""color: blue;"">as dynamic</span>;

    <span style=""color: blue;"">dynamic </span>song = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">JObject</span>();
    song.SongName = <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>;
    song.SongLength = <span style=""color: #a31515;"">""4:11""</span>;
    album.Songs.Add(song);

    song = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">JObject</span>();
    song.SongName = <span style=""color: #a31515;"">""Love at First Feel""</span>;
    song.SongLength = <span style=""color: #a31515;"">""3:10""</span>;
    album.Songs.Add(song);

    <span style=""color: #2b91af;"">Console</span>.WriteLine(album.ToString());
}

运行上面的示例代码,就能形成以下结构的JSON内容:

{ <span style=color: maroon;>Entered: <span style=color: maroon;>2012-08-18T13:26:37.7137482-10:00, <span style=color: maroon;>AlbumName: <span style=color: maroon;>Dirty Deeds Done Dirt Cheap, <span style=color: maroon;>Artist: <span style=color: maroon;>AC/DC, <span style=color: maroon;>YearReleased: 1976, <span style=color: maroon;>Songs: [ { <span style=color: maroon;>SongName: <span style=color: maroon;>Dirty Deeds Done Dirt Cheap, <span style=color: maroon;>SongLength: <span style=color: maroon;>4:11 }, { <span style=color: maroon;>SongName: <span style=color: maroon;>Love at First Feel, <span style=color: maroon;>SongLength: <span style=color: maroon;>3:10 } ] }

JSON.NET提供一个配置选项,可控制如何生成JSON对象,默认值通常是很好的,但您也可以重写JsonSettings进行个性化的设置。

以上代码最重要的是没有明确指定类型,便可将动态对象进行JSON序列化,而JObject对象只是接收数据,具体结构通过动态语言在运行时生成,这意味着此代码可以在运行时被编译,从而体现动态语言的优势。

在这里我使用JObject创建一张专辑对象,并立即将它强制转换为动态类型。JObject()构造行为有点类似于ExpandoObject ,它允许您简单地向对象动态添加属性。在内部,JObject 键值的存储很容易通过IDynamicMetaObject接口暴露出来,从而提供简单的访问方式。

动态对象的语法是非常干净的,你只需要向对象添加属性,不用考虑属性的类型,运行时会自动进行类型推断,对于对象和数组首先需要显式地通过的JObject或者JArray进行创建,然后将其强制转换为动态类型,随后向它动态地添加属性。始终要记住,这些值是动态的,不会被IDE静态编译,因此没有智能感知和类型检查。所以你始终要保证对象的键和值是否拼写错误。

注意:您还可以访问JObject实例直接(非动态)获得基础的JObject类型,这意味着你可以将JSON字符串赋值给属性。

下面是让我们来看看两种风格的相互访问:

<span style=color: green;>// strong type instance <span style=color: blue;>var jsonObject = <span style=color: blue;>new <span style=color: #2b91af;>JObject();

<span style=""color: green;"">// you can explicitly add values here
</span>jsonObject.Add(<span style=""color: #a31515;"">""Entered""</span>, <span style=""color: #2b91af;"">DateTime</span>.Now);

<span style=""color: green;"">// expando style instance you can just 'use' properties
</span><span style=""color: blue;"">dynamic </span>album = jsonObject;

album.AlbumName = <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>;

JContainer(基类为JObject和JArray)是一个集合,实现了IEnumerable接口,因此你还可以轻松地在运行时循环访问:

<span style=color: blue;>foreach (<span style=color: blue;>var item <span style=color: blue;>in jsonObject) { <span style=color: #2b91af;>Console.WriteLine(item.Key + <span style=color: #a31515;>"" "" + item.Value.ToString()); }

通过JObject.Parse()和JArray.Parse()方法导入JSON格式

JValue结构支持Parse()和Load()方法,这两个方法可以分别从字符串或各种流读取JSON数据。JValue包括最核心的JSON 解析能力,支持将字符串转化为我们熟悉的动态对象。下面是一个简单的示例:

<span style=color: blue;>public void JValueParsingTest() { <span style=color: blue;>var jsonString = <span style=color: #a31515;>@{Name:Rick,Company:West Wind, Entered:2012-03-16T00:03:33.245-10:00};

    <span style=""color: blue;"">dynamic </span>json = <span style=""color: #2b91af;"">JValue</span>.Parse(jsonString);

    <span style=""color: green;"">// values require casting
    </span><span style=""color: blue;"">string </span>name = json.Name;
    <span style=""color: blue;"">string </span>company = json.Company;
    <span style=""color: #2b91af;"">DateTime </span>entered = json.Entered;

    <span style=""color: #2b91af;"">Assert</span>.AreEqual(name, <span style=""color: #a31515;"">""Rick""</span>);
    <span style=""color: #2b91af;"">Assert</span>.AreEqual(company, <span style=""color: #a31515;"">""West Wind""</span>);
}

以上示例将JSON字符串转换为成JObject对象,并强制转换为动态类型,在测试方法的末尾进行了断言的比较。

在ASP.NET WEB API中使用JObject和JArray

在ASP.NET WEB API控制器中,我们可以通过这些对象接收JSON格式,或者使用动态语言优势进行JSON转换。

下面的的示例接收动态JSON输入,然后创建一个新的动态JSON对象,并返回第一个对象:

[<span style=color: #2b91af;>HttpPost] <span style=color: blue;>public <span style=color: #2b91af;>JObject PostAlbumJObject(<span style=color: #2b91af;>JObject jAlbum) { <span style=color: green;>// dynamic input from inbound JSON <span style=color: blue;>dynamic album = jAlbum;

    <span style=""color: green;"">// create a new JSON object to write out
    </span><span style=""color: blue;"">dynamic </span>newAlbum = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">JObject</span>();

    <span style=""color: green;"">// Create properties on the new instance
    // with values from the first
    </span>newAlbum.AlbumName = album.AlbumName + <span style=""color: #a31515;"">"" New""</span>;
    newAlbum.NewProperty = <span style=""color: #a31515;"">""something new""</span>;
    newAlbum.Songs = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">JArray</span>();

    <span style=""color: blue;"">foreach </span>(<span style=""color: blue;"">dynamic </span>song <span style=""color: blue;"">in </span>album.Songs)
    {
        song.SongName = song.SongName + <span style=""color: #a31515;"">"" New""</span>;
        newAlbum.Songs.Add(song);
    }

    <span style=""color: blue;"">return </span>newAlbum;
}

<span style=color: #006400;>//向服务器发送请求的原始格式

</span>{
    AlbumName: <span style=""color: maroon;"">""Dirty Deeds""</span>,
    Songs: [
    {
        SongName: <span style=""color: maroon;"">""Problem Child""
    </span>},
    {
        SongName: <span style=""color: maroon;"">""Squealer""
    </span>}]
}

<span style=""color: #006400;"">//服务器返回的数据格式如下:
</span>{
    <span style=""color: maroon;"">""AlbumName""</span>: <span style=""color: maroon;"">""Dirty Deeds New""</span>,
    <span style=""color: maroon;"">""NewProperty""</span>: <span style=""color: maroon;"">""something new""</span>,
    <span style=""color: maroon;"">""Songs""</span>: [
     {
         <span style=""color: maroon;"">""SongName""</span>: <span style=""color: maroon;"">""Problem Child New""
     </span>},
     {
         <span style=""color: maroon;"">""SongName""</span>: <span style=""color: maroon;"">""Squealer New""
     </span>}]
}

当你在WEB API方法中接收或返回JObject、JValue、JToken或JArray实例,MVC API提供的格式化媒体自动进行转换,让代码看起来更优雅。

动态的强类型映射

此外我们还可以将JObject和JArray实例映射到一个强类型的对象,所以你可以在同一段代码中混合书写动态和静态类型,让你乐在其中。

[<span style=color: #2b91af;>TestMethod] <span style=color: blue;>public void JsonParseToStrongTypeTest(<span style=color: blue;>string jsonString) { <span style=color: #2b91af;>JArray albums = <span style=color: #2b91af;>JArray.Parse(jsonString) <span style=color: blue;>as <span style=color: #2b91af;>JArray;

    <span style=""color: green;"">// pick out one album
    </span><span style=""color: #2b91af;"">JObject </span>jalbum = albums[0] <span style=""color: blue;"">as </span><span style=""color: #2b91af;"">JObject</span>;

    <span style=""color: green;"">// Copy to a static Album instance
    </span><span style=""color: #2b91af;"">Album </span>album = jalbum.ToObject<<span style=""color: #2b91af;"">Album</span>>();

    <span style=""color: #2b91af;"">Assert</span>.IsNotNull(album);
    <span style=""color: #2b91af;"">Assert</span>.AreEqual(album.AlbumName, jalbum.Value<<span style=""color: blue;"">string</span>>(<span style=""color: #a31515;"">""AlbumName""</span>));
    <span style=""color: #2b91af;"">Assert</span>.IsTrue(album.Songs.Count > 0);
}

上面的代码将一段JSON数组格式的字符串转换为JArray对象,同时取出数组的第一个元素,将第一个元素转换为静态的强类型对象。

强类型JSON解析

上面介绍了JSON.NET对动态语言的支持,但也别忘了它对静态类型的强大支持,关于如何序列化和反序列化强类型对象,下面提供一个简单的示例:

[<span style=color: #2b91af;>TestMethod] <span style=color: blue;>public void StronglyTypedSerializationTest() {

    <span style=""color: green;"">// Demonstrate deserialization from a raw string
    </span><span style=""color: blue;"">var </span>album = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">Album</span>()
    {
        AlbumName = <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>,
        Artist = <span style=""color: #a31515;"">""AC/DC""</span>,
        Entered = <span style=""color: #2b91af;"">DateTime</span>.Now,
        YearReleased = 1976,
        Songs = <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">List</span><<span style=""color: #2b91af;"">Song</span>>()
        {
         <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">Song</span>()
          {
           SongName = <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>,
           SongLength = <span style=""color: #a31515;"">""4:11""
          </span>},
         <span style=""color: blue;"">new </span><span style=""color: #2b91af;"">Song</span>()
          {
           SongName = <span style=""color: #a31515;"">""Love at First Feel""</span>,
           SongLength = <span style=""color: #a31515;"">""3:10""
          </span>}
        }
    };

    <span style=""color: green;"">// serialize to string
    </span><span style=""color: blue;"">string </span>json2 = <span style=""color: #2b91af;"">JsonConvert</span>.SerializeObject(album, <span style=""color: #2b91af;"">Formatting</span>.Indented);

    <span style=""color: #2b91af;"">Console</span>.WriteLine(json2);

    <span style=""color: green;"">// make sure we can serialize back
    </span><span style=""color: blue;"">var </span>album2 = <span style=""color: #2b91af;"">JsonConvert</span>.DeserializeObject<<span style=""color: #2b91af;"">Album</span>>(json2);

    <span style=""color: #2b91af;"">Assert</span>.IsNotNull(album2);
    <span style=""color: #2b91af;"">Assert</span>.IsTrue(album2.AlbumName == <span style=""color: #a31515;"">""Dirty Deeds Done Dirt Cheap""</span>);
    <span style=""color: #2b91af;"">Assert</span>.IsTrue(album2.Songs.Count == 2);
}

JsonConvert是一个高级别的静态类,包装更低级别的功能,但你也可以使用JsonSerializer类,该类可以序列化和反序列化各种流,它是支持更多一点的工作,为您提供更多的控制。

最后的总结

JSON.NET是一个相当齐全的JSON处理组件,为我们提供了更多的选择,对静态语言和动态语言的支持,复杂的JSON可通过LINQ查询出来,灵活的扩展性,优秀的开源库,值得我们每一个.NET开发人员深入学习。"