Reputation: 5167
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
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