23

I'm consuming a WCF service that returns JSON results wrapped inside the 'd' root element. The JSON response looks like this:

{"d":[
  {
    "__type":"DiskSpaceInfo:#Diagnostics.Common",
    "AvailableSpace":38076567552,
    "Drive":"C:\\",
    "TotalSpace":134789197824
  },
  {
    "__type":"DiskSpaceInfo:#Diagnostics.Common",
    "AvailableSpace":166942183424,
    "Drive":"D:\\",
    "TotalSpace":185149157376
  }
]}

I don't want to use dynamic typing, I have my class Diagnostics.Common.DiskSpaceInfo that I want to use when deserializing.

I am using Json.NET (Netwonsoft JSON).

The question is how to tell it to ignore the root element (that 'd' element) and parse what is inside.

The best solution I have so far is to use an anonymous type:

DiskSpaceInfo[] result = JsonConvert.DeserializeAnonymousType(json, new
    {
        d = new DiskSpaceInfo[0]
    }).d;

this actually works but I don't like it very much. Is there another way? What I would like is something like:

DiskSpaceInfo[] result = JsonConvert.Deserialize(json, skipRoot: true);

or something like that...

Nikolaos Georgiou
  • 2,792
  • 1
  • 26
  • 32
  • +1 because I like your anonymous type solution. – default.kramer Dec 04 '12 at 15:37
  • Related question that asks how to do this without parsing to an intermediate `JToken`: [JSON.NET deserialize a specific property](https://stackoverflow.com/questions/19438472/json-net-deserialize-a-specific-property). – dbc Apr 20 '16 at 15:43

3 Answers3

21

If you know what to search like in this case "d" which is a root node then you can do the following.

JObject jo = JObject.Parse(json);
DiskSpaceInfo[] diskSpaceArray = jo.SelectToken("d", false).ToObject<DiskSpaceInfo[]>();

If you simply want to ignore the root class which you do not know then you can use the "@Giu Do" solution just that you can use test2.ToObject<DiskSpaceInfo[]>(); instead of the Console.Write(test2);

        JObject o = JObject.Parse(json);
        if (o != null)
        {
            var test = o.First;
            if (test != null)
            {
                var test2 = test.First;
                if (test2 != null)
                {
                    DiskSpaceInfo[] diskSpaceArray = test2.ToObject<DiskSpaceInfo[]>();
                }
            }
        }
keyr
  • 990
  • 13
  • 27
  • Thanks! Nice solutions, I tried them out and they work. I'll leave the topic open in case someone offers a different approach and then I'll mark yours as the answer to the topic. However, no solution in particular gives me a strong feeling to like it more over the others (including my original anonymous type approach). Perhaps I can do some benchmarking to see if there's a performance benefit in one of the three ways. – Nikolaos Georgiou Dec 04 '12 at 19:45
3

Following on from previous answers here, I would like to suggest using your own static utility class. This is reusable and will allow you to get the syntax you are looking for.

public static class JsonUtil
{
    public static T Deserialize<T>(string json, bool ignoreRoot) where T : class
    {
        return ignoreRoot 
            ? JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject<T>()
            : JObject.Parse(json)?.ToObject<T>();
    }
}

You would invoke it like this:

var resultA = JsonUtil.Deserialize<DiskSpaceInfo[]>(json, ignoreRoot: true);

or

var resultB = JsonUtil.Deserialize<DiskSpaceInfoRoot>(json, ignoreRoot: false);
Cymatilis
  • 171
  • 1
  • 4
1

By Newtonsoft, I suppose you use JSon.net, here is my solution, I used Linq to JSon available in this framework :

using System;
using Newtonsoft.Json.Linq;

namespace JSonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string json = @"{""d"":[
  {
    ""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
    ""AvailableSpace"":38076567552,
    ""Drive"":""C:\\"",
    ""TotalSpace"":134789197824
  },
  {
    ""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
    ""AvailableSpace"":166942183424,
    ""Drive"":""D:\\"",
    ""TotalSpace"":185149157376
  }
]}";
        JObject o = JObject.Parse(json);

        if (o != null)
        {
            var test = o.First;

            if (test != null)
            {
                var test2 = test.First;
                if (test2 != null)
                {
                    Console.Write(test2);
                }
            }
        }

        Console.Read();

    }
}
}

I have used the property First because you need to find the first node after d, which is the first node of the json you received.

You just have to create a function that reproduice the Main, don't forget to check if the objects are not null to avoid NullReferenceException.

Giu
  • 1,832
  • 2
  • 16
  • 31
  • Thanks Giu Do for the Json.NET naming, I edited it in the original question. Regarding your solution, how do I cast test2 to the correct type DiskSpaceInfo? test2 is still of type JToken. – Nikolaos Georgiou Dec 04 '12 at 15:48