2

I've been struggling to deserialize the following JSON into C#. The JSON returned from the API has quite a few layers, so maybe I am overlooking something.

The property that is null is MemberStats (inside of the NestedStatEntries class). Everything else is okay.

JSON:

{
    "kind": "tm:ltm:pool:members:memberscollectionstats",
    "selfLink": "https://device/mgmt/tm/ltm/pool/myPoolName/members/stats?ver=12.1.2",
    "entries": {
        "https://device/mgmt/tm/ltm/pool/myPoolName/members/~Common~myPoolName/stats": {
            "nestedStats": {
                "kind": "tm:ltm:pool:members:membersstats",
                "selfLink": "https://device/mgmt/tm/ltm/pool/myPoolName/members/~Common~myPoolName/stats?ver=12.1.2",
                "entries": {
                    "activeMemberCnt": {
                        "value": 16
                    },
                    "connqAll.ageEdm": {
                        "value": 0
                    },
                    "connqAll.ageEma": {
                        "value": 0
                    },
                    "connqAll.ageHead": {
                        "value": 0
                    },
                    "connqAll.ageMax": {
                        "value": 0
                    },
                    "connqAll.depth": {
                        "value": 0
                    },
                    "connqAll.serviced": {
                        "value": 0
                    },
                    "connq.ageEdm": {
                        "value": 0
                    },
                    "connq.ageEma": {
                        "value": 0
                    },
                    "connq.ageHead": {
                        "value": 0
                    },
                    "connq.ageMax": {
                        "value": 0
                    },
                    "connq.depth": {
                        "value": 0
                    },
                    "connq.serviced": {
                        "value": 0
                    },
                    "curSessions": {
                        "value": 0
                    },
                    "minActiveMembers": {
                        "value": 0
                    },
                    "monitorRule": {
                        "description": "min 1 of /Common/prod-olbtp_https"
                    },
                    "tmName": {
                        "description": "/Common/myPoolName"
                    },
                    "serverside.bitsIn": {
                        "value": 1854170442864
                    },
                    "serverside.bitsOut": {
                        "value": 28010721155520
                    },
                    "serverside.curConns": {
                        "value": 46
                    },
                    "serverside.maxConns": {
                        "value": 350
                    },
                    "serverside.pktsIn": {
                        "value": 1486804119
                    },
                    "serverside.pktsOut": {
                        "value": 2494389460
                    },
                    "serverside.totConns": {
                        "value": 2864936
                    },
                    "status.availabilityState": {
                        "description": "available"
                    },
                    "status.enabledState": {
                        "description": "enabled"
                    },
                    "status.statusReason": {
                        "description": "The pool is available"
                    },
                    "totRequests": {
                        "value": 0
                    },
                    "https://device/mgmt/tm/ltm/pool/myPoolName/members/~Common~myPoolName/members/stats": {
                        "nestedStats": {
                            "entries": {
                                "https://device/mgmt/tm/ltm/pool/myPoolName/members/~Common~myPoolName/members/~Common~10.10.10.30:443/stats": {
                                    "nestedStats": {
                                        "entries": {
                                            "addr": {
                                                "description": "10.10.10.30"
                                            },
                                            "connq.ageEdm": {
                                                "value": 0
                                            },
                                            "connq.ageEma": {
                                                "value": 0
                                            },
                                            "connq.ageHead": {
                                                "value": 0
                                            },
                                            "connq.ageMax": {
                                                "value": 0
                                            },
                                            "connq.depth": {
                                                "value": 0
                                            },
                                            "connq.serviced": {
                                                "value": 0
                                            },
                                            "curSessions": {
                                                "value": 0
                                            },
                                            "monitorRule": {
                                                "description": "min 1 of /Common/prod-olbtp_https (pool monitor)"
                                            },
                                            "monitorStatus": {
                                                "description": "up"
                                            },
                                            "nodeName": {
                                                "description": "/Common/10.10.10.30"
                                            },
                                            "poolName": {
                                                "description": "/Common/myPoolName"
                                            },
                                            "port": {
                                                "value": 443
                                            },
                                            "serverside.bitsIn": {
                                                "value": 123606959904
                                            },
                                            "serverside.bitsOut": {
                                                "value": 1252550265608
                                            },
                                            "serverside.curConns": {
                                                "value": 4
                                            },
                                            "serverside.maxConns": {
                                                "value": 18
                                            },
                                            "serverside.pktsIn": {
                                                "value": 75556262
                                            },
                                            "serverside.pktsOut": {
                                                "value": 115991280
                                            },
                                            "serverside.totConns": {
                                                "value": 189971
                                            },
                                            "sessionStatus": {
                                                "description": "enabled"
                                            },
                                            "status.availabilityState": {
                                                "description": "available"
                                            },
                                            "status.enabledState": {
                                                "description": "enabled"
                                            },
                                            "status.statusReason": {
                                                "description": "Pool member is available"
                                            },
                                            "totRequests": {
                                                "value": 0
                                            }
                                        }
                                    }
                                },
                                "https://device/mgmt/tm/ltm/pool/myPoolName/members/~Common~myPoolName/members/~Common~10.10.10.40:443/stats": {
                                    "nestedStats": {
                                        "entries": {
                                            "addr": {
                                                "description": "10.10.10.40"
                                            },
                                            "connq.ageEdm": {
                                                "value": 0
                                            },
                                            "connq.ageEma": {
                                                "value": 0
                                            },
                                            "connq.ageHead": {
                                                "value": 0
                                            },
                                            "connq.ageMax": {
                                                "value": 0
                                            },
                                            "connq.depth": {
                                                "value": 0
                                            },
                                            "connq.serviced": {
                                                "value": 0
                                            },
                                            "curSessions": {
                                                "value": 0
                                            },
                                            "monitorRule": {
                                                "description": "min 1 of /Common/prod-olbtp_https (pool monitor)"
                                            },
                                            "monitorStatus": {
                                                "description": "up"
                                            },
                                            "nodeName": {
                                                "description": "/Common/10.10.10.40"
                                            },
                                            "poolName": {
                                                "description": "/Common/myPoolName"
                                            },
                                            "port": {
                                                "value": 443
                                            },
                                            "serverside.bitsIn": {
                                                "value": 70383109304
                                            },
                                            "serverside.bitsOut": {
                                                "value": 1114184738168
                                            },
                                            "serverside.curConns": {
                                                "value": 3
                                            },
                                            "serverside.maxConns": {
                                                "value": 32
                                            },
                                            "serverside.pktsIn": {
                                                "value": 58376213
                                            },
                                            "serverside.pktsOut": {
                                                "value": 98729511
                                            },
                                            "serverside.totConns": {
                                                "value": 103410
                                            },
                                            "sessionStatus": {
                                                "description": "enabled"
                                            },
                                            "status.availabilityState": {
                                                "description": "available"
                                            },
                                            "status.enabledState": {
                                                "description": "enabled"
                                            },
                                            "status.statusReason": {
                                                "description": "Pool member is available"
                                            },
                                            "totRequests": {
                                                "value": 0
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

C# Wrapper:

public class PoolAndMemberStatistics
{
    [JsonProperty("entries")]
    public Dictionary<string, EntriesWrapper> Entries { get; set; }

    [JsonProperty("generation")]
    public long Generation { get; set; }

    [JsonProperty("kind")]
    public string Kind { get; set; }

    [JsonProperty("selfLink")]
    public string SelfLink { get; set; }

    public partial class EntriesWrapper
    {
        [JsonProperty("nestedStats")]
        public NestedStats NestedStats { get; set; }
    }

    public partial class NestedStats
    {
        [JsonProperty("entries")]
        public NestedStatEntries Entries { get; set; }

        [JsonProperty("kind")]
        public string Kind { get; set; }

        [JsonProperty("selfLink")]
        public string SelfLink { get; set; }
    }

    public partial class NestedStatEntries
    {
        [JsonProperty("nestedStats")]
        public Dictionary<string, NestedStats> MemberStats { get; set; }

        [JsonProperty("activeMemberCnt")]
        public Dictionary<string, long> ActiveMemberCnt { get; set; }

        [JsonProperty("connq.ageEdm")]
        public Dictionary<string, long> ConnqAgeEdm { get; set; }

        [JsonProperty("connq.ageEma")]
        public Dictionary<string, long> ConnqAgeEma { get; set; }

        [JsonProperty("connq.ageHead")]
        public Dictionary<string, long> ConnqAgeHead { get; set; }

        [JsonProperty("connq.ageMax")]
        public Dictionary<string, long> ConnqAgeMax { get; set; }

        [JsonProperty("connqAll.ageEdm")]
        public Dictionary<string, long> ConnqAllAgeEdm { get; set; }

        [JsonProperty("connqAll.ageEma")]
        public Dictionary<string, long> ConnqAllAgeEma { get; set; }

        [JsonProperty("connqAll.ageHead")]
        public Dictionary<string, long> ConnqAllAgeHead { get; set; }

        [JsonProperty("connqAll.ageMax")]
        public Dictionary<string, long> ConnqAllAgeMax { get; set; }

        [JsonProperty("connqAll.depth")]
        public Dictionary<string, long> ConnqAllDepth { get; set; }

        [JsonProperty("connqAll.serviced")]
        public Dictionary<string, long> ConnqAllServiced { get; set; }

        [JsonProperty("connq.depth")]
        public Dictionary<string, long> ConnqDepth { get; set; }

        [JsonProperty("connq.serviced")]
        public Dictionary<string, long> ConnqServiced { get; set; }

        [JsonProperty("curSessions")]
        public Dictionary<string, long> CurSessions { get; set; }

        [JsonProperty("minActiveMembers")]
        public Dictionary<string, long> MinActiveMembers { get; set; }

        [JsonProperty("monitorRule")]
        public Dictionary<string, string> MonitorRule { get; set; }

        [JsonProperty("serverside.bitsIn")]
        public Dictionary<string, long> ServersideBitsIn { get; set; }

        [JsonProperty("serverside.bitsOut")]
        public Dictionary<string, long> ServersideBitsOut { get; set; }

        [JsonProperty("serverside.curConns")]
        public Dictionary<string, long> ServersideCurConns { get; set; }

        [JsonProperty("serverside.maxConns")]
        public Dictionary<string, long> ServersideMaxConns { get; set; }

        [JsonProperty("serverside.pktsIn")]
        public Dictionary<string, long> ServersidePktsIn { get; set; }

        [JsonProperty("serverside.pktsOut")]
        public Dictionary<string, long> ServersidePktsOut { get; set; }

        [JsonProperty("serverside.totConns")]
        public Dictionary<string, long> ServersideTotConns { get; set; }

        [JsonProperty("status.availabilityState")]
        public Dictionary<string, string> StatusAvailabilityState { get; set; }

        [JsonProperty("status.enabledState")]
        public Dictionary<string, string> StatusEnabledState { get; set; }

        [JsonProperty("status.statusReason")]
        public Dictionary<string, string> StatusStatusReason { get; set; }

        [JsonProperty("tmName")]
        public Dictionary<string, string> TmName { get; set; }

        [JsonProperty("totRequests")]
        public Dictionary<string, long> TotRequests { get; set; }
    }
}
ViKiNG
  • 1,294
  • 2
  • 19
  • 26
blgrnboy
  • 4,877
  • 10
  • 43
  • 94
  • I believe that simple call like this `PoolAndMemberStatistics obj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);` won’t help you because `Newtonsoft.Json.JsonConvert.DeserializeObject` simply couldn’t manage recursive JSON returned by your API. Possibly this post may help: [link](https://stackoverflow.com/questions/5546142/how-do-i-use-json-net-to-deserialize-into-nested-recursive-dictionary-and-list) I afraid you may end up writing your custom JsonConverter – Oleksandr Tyshchenko Nov 15 '17 at 22:06

1 Answers1

2

Indeed, this JSON structure does not seem very friendly at first look. It definitely defies code generation using tools like json2csharp.com. But on closer inspection it looks like we have a recursive structure here which can be boiled down to two classes. First, we have a Statistics class, which has a dictionary of Entries (along with a couple of other optional properties). Secondly, we have an Entry class, which contains either a numeric Value, a string Description or a NestedStats, the latter of which is a Statistics instance.

class Statistics
{
    [JsonProperty("entries")]
    public Dictionary<string, Entry> Entries { get; set; }

    [JsonProperty("kind")]
    public string Kind { get; set; }

    [JsonProperty("selfLink")]
    public string SelfLink { get; set; }
}

class Entry
{
    [JsonProperty("value")]
    public long Value { get; set; }

    [JsonProperty("description")]
    public string Description { get; set; }

    [JsonProperty("nestedStats")]
    public Statistics NestedStats { get; set; }
}

To the Entry class I would maybe add a read-only EntryType property to indicate which of the three values is populated, but this is optional.

enum EntryType { Value, Description, NestedStats }

class Entry
{
    ...

    public EntryType EntryType
    {
        get
        {
            if (NestedStats != null) return EntryType.NestedStats;
            if (Description != null) return EntryType.Description;
            return EntryType.Value;
        }
    }
}

You can deserialize the whole JSON structure like this:

Statistics stats = JsonConvert.DeserializeObject<Statistics>(json);

(Contrary to what was said in the comments, Json.Net can handle a recursive structure just fine.)

Fiddle: https://dotnetfiddle.net/aB1qbn

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300