Search code examples
c#jsonjson.net

Getting stuck reading JSON Children using Newtonsoft.json


I'm having problems reading Children of the root element and understanding where I'm going wrong in the JSON below:

{
  "001": {
    "peers": [
      {
        "id": 0,
        "server": "1.1.1.1:80",
        "name": "1.1.1.1:80",
        "backup": false,
        "weight": 1,
        "state": "up",
        "active": 0,
        "requests": 0,
        "responses": {
          "1xx": 0,
          "2xx": 0,
          "3xx": 0,
          "4xx": 0,
          "5xx": 0,
          "total": 0
        },
        "sent": 0,
        "received": 0,
        "fails": 0,
        "unavail": 0,
        "health_checks": {
          "checks": 0,
          "fails": 0,
          "unhealthy": 0
        },
        "downtime": 0
      },
      {
        "id": 1,
        "server": "127.0.0.1:8888",
        "name": "127.0.0.1:8888",
        "backup": false,
        "weight": 1,
        "state": "down",
        "active": 0,
        "requests": 0,
        "responses": {
          "1xx": 0,
          "2xx": 0,
          "3xx": 0,
          "4xx": 0,
          "5xx": 0,
          "total": 0
        },
        "sent": 0,
        "received": 0,
        "fails": 0,
        "unavail": 0,
        "health_checks": {
          "checks": 0,
          "fails": 0,
          "unhealthy": 0
        },
        "downtime": 0
      }
    ],
    "keepalive": 0,
    "zombies": 0,
    "zone": "001"
  },
  "002": {
    "peers": [
      {
        "id": 0,
        "server": "1.1.1.2:80",
        "name": "1.1.1.2:80",
        "backup": false,
        "weight": 1,
        "state": "up",
        "active": 0,
        "requests": 0,
        "responses": {
          "1xx": 0,
          "2xx": 0,
          "3xx": 0,
          "4xx": 0,
          "5xx": 0,
          "total": 0
        },
        "sent": 0,
        "received": 0,
        "fails": 0,
        "unavail": 0,
        "health_checks": {
          "checks": 0,
          "fails": 0,
          "unhealthy": 0
        },
        "downtime": 0
      },
      {
        "id": 1,
        "server": "127.0.0.1:8888",
        "name": "127.0.0.1:8888",
        "backup": false,
        "weight": 1,
        "state": "down",
        "active": 0,
        "requests": 0,
        "responses": {
          "1xx": 0,
          "2xx": 0,
          "3xx": 0,
          "4xx": 0,
          "5xx": 0,
          "total": 0
        },
        "sent": 0,
        "received": 0,
        "fails": 0,
        "unavail": 0,
        "health_checks": {
          "checks": 0,
          "fails": 0,
          "unhealthy": 0
        },
        "downtime": 0
      }
    ],
    "keepalive": 0,
    "zombies": 0,
    "zone": "002"
  }
}

I know I can pass the JSON into a JObject and find the server value below by naming the 001 JSON object:

JObject obj = JObject.Parse(jsonResponse);

var h = obj.Value<JObject>("001").Value<JArray>("peers")[0].SelectToken("server").ToString();

However I'd like to read the children of the root element (which I think are objects 001 and 002) regardless of their names, foreach through them to find values for the keys "zone" and "keepalive". I thought I could do this:

List<JToken> toks = obj.Root.Children().ToList<JToken>();

string output = "";
foreach (JToken token in toks)
{
    JProperty prop = (JProperty)token;
    output += prop.Value<string>("zone");  // returns nothing
    string bla = token.ToString();  //returns 001 and all it's child objects
}

But the output string is blank. I can see that if I try token.ToString() the JToken object has stuff in it, but I can't seem to read it.

If I use Visual studio to create an object for Deserialization I get this for the root object which confuses me further:

public class Rootobject
{
    public 001 001 { get; set; }
    public 002 002 { get; set; }
}

What am I doing wrong? Any help would be greatly appreciated.

Jon


Solution

  • If you're trying to parse this JSON using JTokens, you need to understand the different kinds of possible tokens and the relationships between them. You would probably benefit from reading this answer.

    At the root you have an object; this object has two properties called "001" and "002". Those properties' respective values are objects again, containing their own properties ("peers", "keepalive", "zombies" and "zone"). And so on.

    If you ultimately want to loop through the root properties without knowing their names and then dump out the values of "zone" and "keepalive" properties of the child objects, you can do it like this:

    var obj = JObject.Parse(json);
    foreach (JProperty prop in obj.Properties())
    {
        var item = (JObject)prop.Value;
        var zone = (string)item["zone"];
        var keepalive = (int)item["keepalive"];
        Console.WriteLine($"Zone: {zone} keepalive: {keepalive}");
    }
    

    Fiddle: https://dotnetfiddle.net/tNut9v

    If JTokens make your head spin, then you might be better off declaring classes for the JSON. The only wrinkle is the dynamic property names in the root ("001", "002"). But you can handle that by deserializing to a Dictionary<string, T> where T is an Item class as shown below. Define the classes like this:

    public class Item
    {
        public List<Peer> peers { get; set; }
        public int keepalive { get; set; }
        public int zombies { get; set; }
        public string zone { get; set; }
    }
    
    public class Peer
    {
        public int id { get; set; }
        public string server { get; set; }
        public string name { get; set; }
        public bool backup { get; set; }
        public int weight { get; set; }
        public string state { get; set; }
        public int active { get; set; }
        public int requests { get; set; }
        public Responses responses { get; set; }
        public int sent { get; set; }
        public int received { get; set; }
        public int fails { get; set; }
        public int unavail { get; set; }
        public HealthChecks health_checks { get; set; }
        public int downtime { get; set; }
    }
    
    public class Responses
    {
        [JsonProperty("1xx")]
        public int code_1xx { get; set; }
        [JsonProperty("2xx")]
        public int code_2xx { get; set; }
        [JsonProperty("3xx")]
        public int code_3xx { get; set; }
        [JsonProperty("4xx")]
        public int code_4xx { get; set; }
        [JsonProperty("5xx")]
        public int code_5xx { get; set; }
        public int total { get; set; }
    }
    
    public class HealthChecks
    {
        public int checks { get; set; }
        public int fails { get; set; }
        public int unhealthy { get; set; }
    }
    

    Then you can deserialize and dump out the data you want like this:

    var dict = JsonConvert.DeserializeObject<Dictionary<string, Item>>(json);
    foreach (var kvp in dict)
    {
        Item item = kvp.Value;
        Console.WriteLine($"Zone: {item.zone} keepalive: {item.keepalive}");
    }
    

    Fiddle: https://dotnetfiddle.net/3rRmxJ