1

How can I query (to see if a property exists) and enumerate (the array property) found within a complex JSON object using using JSON.NET in C# ?

I am receiving a complex JSON object from an API with a variable number/type of properties.

I keep reading the JSON.Net Documentation, reviewing samples, etc. but not gotten far and am lost in JObject, JArray, JToken, using dynamic, etc...

I want to find the pageResponses.scriptOutput property, verify it contains and .items[] array and then enumerate/iterate the array.

Edit

I made progress and found typo in JSON data example.

But how can I query/enumerate the child objects using key names, e.g.(item.location, item.timestamp) ?

string json = File.ReadAllText(@"Output.json");
JObject jObj = JObject.Parse(json);

IList<JToken> items = jObj["pageResponses"][0]["scriptOutput"]["items"].ToList();
foreach (JToken item in items){
    Console.WriteLine(item["location"]);
}
/*** Console Output ***/
// Austin, TX
// Anaheim, CA
// Adams, MN
// Barstow, CA

var varItems = from o in jObj["pageResponses"][0]["scriptOutput"]["items"].ToList() select o;

foreach (var item in varItems){
    Console.WriteLine(item["timestamp"]);
}
/*** Console Output ***/
// 2016 - 05 - 03 19:53
// 2016 - 05 - 04 04:10
// 2016 - 05 - 04 08:18
// 2016 - 05 - 01 12:26

(JSON sample below trimmed down for brevity)

{
  "meta": {
    "outputAsJson": true,
    "backend": {
      "os": "linux",
      "id": "10.240.0.3_2",
      "requestsProcessed": 8
    }
  },
  "pageResponses": [
    {
      "pageRequest": {
        "renderType": "script",
        "outputAsJson": true
      },
      "frameData": {
        "name": "",
        "childCount": 1
      },
      "events": [
                  {
                    "key": "navigationRequested",
                    "time": "2016-05-06T13:43:30.344Z"
                  },
                  {
                    "key": "navigationRequested",
                    "time": "2016-05-06T13:43:31.131Z"
                  }
      ],
      "scriptOutput": {
        "items": [
          {
            "location": "Austin, TX",
            "timestamp": "2016-05-03 19:53",
            "title": "User Login"
          },
          {
            "location": "Anaheim, CA",
            "timestamp": "2016-05-04 04:10",
            "title": "User Logout"
          },
          {
            "location": "Adams, MN",
            "timestamp": "2016-05-04 08:18",
            "title": "User Login"
          },
          {
            "location": "Barstow, CA",
            "timestamp": "2016-05-01 12:26",
            "title": "User Logout"
          }
        ]
      },
      "statusCode": 200
    }
  ],
  "statusCode": 200,
  "content": {
    "name": "content.json",
    "encoding": "utf8"
  },
  "originalRequest": {
    "pages": [
      {
        "renderType": "script",
        "outputAsJson": true
      }
    ]
  }
}
Newport99
  • 483
  • 4
  • 21
  • 1
    You can create a class hierarchy and for any property you don't want to deserialise you can use `dynamic`. If you want to go through the hierarchy you will need to use a recursive function. – Callum Linington May 06 '16 at 15:11
  • 1
    Another important note, if you are validating JSON, then you can use the JSON.Net validator against a scheme that you create. Look [here](http://www.jsonschemavalidator.net/) – Callum Linington May 06 '16 at 15:12
  • 1
    Look at the `parse` method in this [documentation](http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Schema_JsonSchema.htm) – Callum Linington May 06 '16 at 15:14
  • 1
    I would seriously look in json schema because that sounds like exactly what you're asking for: `I want to find the pageResponses.scriptOutput property, verify it contains` – Callum Linington May 06 '16 at 15:16

2 Answers2

2

I suggest creating a proxy class (I used json2csharp):

public class Backend
{
    public string os { get; set; }
    public string id { get; set; }
    public int requestsProcessed { get; set; }
}

public class Meta
{
    public bool outputAsJson { get; set; }
    public Backend backend { get; set; }
}

public class PageRequest
{
    public string renderType { get; set; }
    public bool outputAsJson { get; set; }
}

public class FrameData
{
    public string name { get; set; }
    public int childCount { get; set; }
}

public class Event
{
    public string key { get; set; }
    public string time { get; set; }
}

public class ScriptOutput
{
    public List<object> items { get; set; }
}

public class PageRespons
{
    public PageRequest pageRequest { get; set; }
    public FrameData frameData { get; set; }
    public List<Event> events { get; set; }
    public ScriptOutput scriptOutput { get; set; }
    public int statusCode { get; set; }
}

public class Content
{
    public string name { get; set; }
    public string encoding { get; set; }
}

public class Page
{
    public string renderType { get; set; }
    public bool outputAsJson { get; set; }
}

public class OriginalRequest
{
    public List<Page> pages { get; set; }
}

public class RootObject
{
    public Meta meta { get; set; }
    public List<PageRespons> pageResponses { get; set; }
    public int statusCode { get; set; }
    public Content content { get; set; }
    public OriginalRequest originalRequest { get; set; }
}

Then deserialize it:

var obj = JsonConvert.DeserializeObject<RootObject>(json);
if (obj != null && obj.pageResponses != null)
{
    foreach (var pageResponse in obj.pageResponses)
    {
        if (pageResponse.scriptOutput == null)
            continue;

        foreach (var item in pageResponse.scriptOutput.items)
        {
            Console.WriteLine(item);
        }
    }
}
Bruno Garcia
  • 6,029
  • 3
  • 25
  • 38
  • This helps little, how to actually check for and enumerate the array? – Newport99 May 06 '16 at 15:37
  • I updated the answer. Is it more clear now? You can use the debugger and inspect the object's structure to help you write the code that handles it. – Bruno Garcia May 06 '16 at 15:40
  • What is the namespace for RootObject? I can't find it anywhere in JSON.NET or .NET docs online. – Newport99 May 06 '16 at 15:48
  • That'd be your class. I pasted all the classes you'd need to add to your solution. – Bruno Garcia May 06 '16 at 15:49
  • Not to be a pain in the "class" but the JSON object has a variable number of properties. I definitely don't want to build a class to encapsulate the entire complex unreliable schema - I am only interested in the pageResponse.scriptOutput (if it exists) and the .items. Can I create a class and deserialize just a single property (pageResponse) ? – Newport99 May 06 '16 at 16:00
  • 1
    Include only the types you need then. No need to add all classes/properties you are not interested in. – Bruno Garcia May 06 '16 at 17:15
  • 1
    Thanks Bruno! This works like a charm in only 9 lines of code. And sorry man I didn't see RootObject in your example (had to scroll down duh!). I created just the classes I need and your advice worked great. `var obj = JsonConvert.DeserializeObject(json); if (obj != null && obj.pageResponses != null){ foreach (var pageResponse in obj.pageResponses){ if (pageResponse.scriptOutput == null)continue; foreach (var item in pageResponse.scriptOutput.items){ Console.WriteLine(item); } } }` – Newport99 May 06 '16 at 17:51
  • Glad it helped. Consider accepting the answer if it's the appropriate solution to you question. – Bruno Garcia May 06 '16 at 18:11
  • Wow I've been checking stackoverflow for years..only asked 7 questions mostly this year....and never knew how to "accept" an answer! (Its' just not that obvious), I was upvoting to accept the answer! I had to [look at the help documentation](http://stackoverflow.com/help/someone-answers) to figure it out. – Newport99 May 06 '16 at 20:06
0

I do this with a couple of Extension Methods and I use JsonConvert.DeserializeObject.

Code snippets below.

Usage

ExpandoObject data = JsonConvert.DeserializeObject<ExpandoObject>(jsonString);
if(data.HasProperty("propertyToCheck"))
{
   object[] objects = data.Get<object[]>("propertyToCheck");
}

In the snippet above I check a property exists, then I assign it to a .Net type, in this case an object array. Though it can be any type so long as it's sane.

Extension Methods

public static bool HasProperty(this ExpandoObject value, string property)
{
    bool hasProp = false;
    if (((IDictionary<String, object>)value).ContainsKey(property))
    {
        hasProp = true;
    }
    return hasProp;
}

public static T Get<T>(this ExpandoObject value, string property)
{
    return (T)((IDictionary<String, dynamic>)value)[property];
}

Quick, easy and to the point!

Daniel Lane
  • 2,575
  • 2
  • 18
  • 33