18

I'm trying to deserialize some JSON data into objects for an application. Up until now it's been fine because the properties on the JSON data was static (key with a value). Now I've got a result where the key is a dynamic piece of data.

Here's an example JSON url:

http://en.wikipedia.org/w/api.php?action=query&format=json&pageids=6695&prop=info

The resulting JSON for this is:

{ "query" : { "pages" : { "6695" : { "counter" : "",
          "lastrevid" : 468683764,
          "length" : 8899,
          "ns" : 0,
          "pageid" : 6695,
          "title" : "Citadel",
          "touched" : "2012-01-03T19:16:16Z"
        } } } }

Okay, that's great except I can't deserialize the "pages" data into an object. If I were to define a class for the pages it would have to look like this:

public class 6695
{
    public string counter { get; set; }
    public int lastrevid { get; set; }
    public int length { get; set; }
    public int ns { get; set; }
    public int pageid { get; set; }
    public string title { get; set; }
    public string touched { get; set; }
}

In order to deserialze the contents (using JsonConvert.Deserialize(jsondata)) and we all know we can't have a class called 6695. Not only that, the name of the class would have to be different (for example pageid=7145 would have to be the 7145 class).

I can seem to pluck some values out if I use something like JObject.Parse(content) and then access items as JArrays but it's pretty ugly and I'm still stuck on trying to get out the data from the pages array.

Looking for someone to help with this. I don't think it's uncommon, it's just not JSON data I've come across before and not sure how to handle it.

Thanks!

PS forgot to mention this is on Windows Phone 7 so "dynamic" isn't available!

Bil Simser
  • 1,713
  • 1
  • 19
  • 27
  • Wouldn't the 6695, just be property name, rather than a class name? Still a problem, but maybe you can do search and replace of the numeric property into some fixed name. I'm not sure how Json.NET handles this but maybe you can try deserialize it as a dictionary of a specific object type? – Rick Strahl Jan 05 '12 at 05:33
  • I'm probably a bit late to this party but the newtonsoft lib has JObject for this ... var obj = JObject.Parse(jsonString); basically treats the data like a complex nested dictionary similar to how javascript behaves in the browser. You get back an instance of a "JObject" which lets you access everything using array syntax eg: var page = obj["pages"]["6695"] – War Feb 02 '16 at 12:27

6 Answers6

27

The simplest method. In this particular case would probably be to go dynamic.

dynamic data = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
var lastRevId = data.query.pages["6695"].lastrevid;

You can reference any element by it's [] name so you can do something like data["query"]["pages"]["6695"]["lastrevid"]. This will get by all those little objects where the name isn't valid in c#.

Richard Dingwall
  • 2,692
  • 1
  • 31
  • 32
Buildstarted
  • 26,529
  • 10
  • 84
  • 95
  • 2
    Looks like this is for wp7 which doesn't support dynamic :( I'll leave this here for anyone else who isn't constrained by c#3 – Buildstarted Jan 05 '12 at 05:48
  • You **don't need to use the generic** method, you can simply go like `dynamic data = JsonConvert.DeserializeObject(json);` – Yves M. Apr 08 '14 at 15:20
19

Here is how you do using https://github.com/facebook-csharp-sdk/simple-json ( https://nuget.org/packages/SimpleJson ).

var text = "{\"query\":{\"pages\":{\"6695\":{\"pageid\":6695,\"ns\":0,\"title\":\"Citadel\",\"touched\":\"2012-01-03T19:16:16Z\",\"lastrevid\":468683764,\"counter\":\"\",\"length\":8899}}}}";

(Using dynamic)

dynamic json = SimpleJson.DeserializeObject(text);
string title = json.query.pages["6695"].title;

foreach (KeyValuePair<string, dynamic> page in json.query.pages)
{
    var id = page.Key;
    var pageId = page.Value.pageid;
    var ns = page.Value.ns;
}

(Using strongly typed classes)

class result
{
    public query query { get; set; }
}
class query
{
    public IDictionary<string, page> pages { get; set; }
}
class page
{
    public long pageid { get; set; }
    public string title { get; set; }
}

var result = SimpleJson.DeserializeObject<result>(text);

[Update]

on windows phone where dynamic is not supported and you don't want to use strongly typed classes.

var json = (IDictionary<string, object>)SimpleJson.DeserializeObject(text);
var query = (IDictionary<string, object>)json["query"];
var pages = (IDictionary<string, object>)query["pages"];
var pageKeys = pages.Keys;
var page = (IDictionary<string, object>)pages["6695"];
var title = (string)page["title"];
jgillich
  • 71,459
  • 6
  • 57
  • 85
prabir
  • 7,674
  • 4
  • 31
  • 43
  • Great! This works (Json.NET is the same and can use strongly typed classes the same was that SimpleJson does). Thanks. – Bil Simser Jan 05 '12 at 06:08
  • I just ran into this problem trying to parse out Gist file details from GitHub. Thank you so much for the answer here. I didn't think to use IDictionary! – Scott Smith Apr 18 '12 at 21:29
2

I hope the below example will help. I always design a model that match the json. It is much better to work with the object when it is your own model design.

It is very easy to generate the c# model from the json. I use this website to generate the model: http://json2csharp.com

A complete example is:

C# Code:

    var targetsObject = Newtonsoft.Json.JsonConvert.DeserializeObject<YourModel>(jsonString);

JSON:

    {
      "investors": [
        {
          "name": "06",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": "5.5"
            },
            {
              "name": "VA IRRRL",
              "value": "6.0"
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "07",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": "7.0"
            },
            {
              "name": "VA",
              "value": "5.5"
            },
            {
              "name": "VA IRRRL",
              "value": ""
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "08",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": "7.0"
            },
            {
              "name": "VA",
              "value": "5.5"
            },
            {
              "name": "VA IRRRL",
              "value": ""
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "09",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": "5.5"
            },
            {
              "name": "VA IRRRL",
              "value": ""
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "10",
          "programs": [
            {
              "name": "Conventional",
              "value": ""
            },
            {
              "name": "FHA - Standard",
              "value": ""
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": ""
            },
            {
              "name": "VA IRRRL",
              "value": ""
            },
            {
              "name": "Non-Prime",
              "value": "2.0"
            }
          ]
        },
        {
          "name": "11",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": "6.0"
            },
            {
              "name": "VA IRRRL",
              "value": "6.0"
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "12",
          "programs": [
            {
              "name": "Conventional",
              "value": "3.5"
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": "5.5"
            },
            {
              "name": "VA IRRRL",
              "value": "6.0"
            },
            {
              "name": "Non-Prime",
              "value": ""
            }
          ]
        },
        {
          "name": "13",
          "programs": [
            {
              "name": "Conventional",
              "value": ""
            },
            {
              "name": "FHA - Standard",
              "value": "5.0"
            },
            {
              "name": "FHA - Streamline",
              "value": ""
            },
            {
              "name": "VA",
              "value": ""
            },
            {
              "name": "VA IRRRL",
              "value": ""
            },
            {
              "name": "Non-Prime",
              "value": "2.0"
            }
          ]
        }
      ]
    }

Model:

    public class Program
    {
        public string name { get; set; }
        public string value { get; set; }
    }

    public class Investor
    {
        public string name { get; set; }
        public List<Program> programs { get; set; }
    }

    public class RootObject
    {
        public List<Investor> investors { get; set; }
    }
Nour Lababidi
  • 414
  • 4
  • 7
1

Using Json.net you can just do:

Dictionary<string,object> result = JsonConvert.DeserializeObject<Dictionary<string,object>>(json);
foreach(var item in result)
    Console.WriteLine(item.Key + " " + item.Value);
N_A
  • 19,799
  • 4
  • 52
  • 98
0

Maybe you could just use one reserved attribute to contain the object type, and then use the base type as shown in this article: Dynamic types with JSON.NET

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
0

How about a simple search and replace in the JSON string ? While it might not be the most elegant solution, it would possibly be the most pragmatic one.

Tom
  • 511
  • 3
  • 12