blgrnboy
blgrnboy

Reputation: 5167

C# JSON Deserialization null nested property

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; }
    }
}

Upvotes: 2

Views: 1260

Answers (1)

Brian Rogers
Brian Rogers

Reputation: 129777

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

Upvotes: 2

Related Questions