'C# find JSON value based only on Key name through multiple levels of array

I have a variety of input JSON formatted data which all contain a particular key-name terminalSize. This is the only piece I know. The total number of JSON trees or the exact depth of terminalSize inside the JSON tree will forever be an unknown and subject to change.

I'm looking for a C# solution to loop through every child of the JSON string and find terminalSize then fetch the value.

I've tried this with success but it will only work if terminalSize is in the first level of the JSON:

var list = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(jsonString);
var dict = list.SelectMany(d => d).ToDictionary(p => p.Key, p => p.Value);
var terminal = dict["terminalSize"];

Example 1.

{
  "status": "success",
  "data": {
    "terminalSize": 3766505.46,
    "totalTerminalSize": 3766505.46
  },
  "message": null
}

Example 2.

{
  "lastUpdated": 1588020678,
  "terminalData": {
    "terminalSize": "451679852",
    "totalTerminalSize": "2100000000"
  },
  "terminalValueSeries": {
    "8x7": 2.33,
    "8x6": 3.73,
    "8x5": 4.49,
    "8x4": 3.68,
    "8x3": 13998,
    "8x2": 274936,
    "8x1": 5.09
  }
}

Example 3.

{
  "terminalSize": "492612346.17",
  "terminalStatus": "online"
}


Solution 1:[1]

You could also use linq and filter the JProperty collection based on JProperty.Name. For example

var result = JObject.Parse(jsonString)
                    .DescendantsAndSelf()
                    .OfType<JProperty>()
                    .Single(x=>x.Name.Equals("terminalSize"))
                    .Value;

Solution 2:[2]

You can parse your JSON to a JToken, then use SelectToken with the recursive descent JsonPath operator .. to get the terminalSize anywhere in the JSON:

var terminalSize = (double?) JToken.Parse(json).SelectToken("$..terminalSize");

Fiddle: https://dotnetfiddle.net/5ziYbP

If there might be multiple terminalSize keys in the JSON, for example if you had an array of terminals, you can use SelectTokens instead and put the terminal sizes into a Dictionary keyed by path:

var sizes = JToken.Parse(json4)
                  .SelectTokens("$..terminalSize")
                  .ToDictionary(t => t.Path, t => (double)t);

Fiddle: https://dotnetfiddle.net/ivSM88

Solution 3:[3]

You may parse your JSON into JObject, then recursively go through all properties and sub objects to find a terminalSize value. There is no need to deserialize the entire JSON into specific object

var json = JObject.Parse(jsonString);
var result = GetTerminalSize(json);

double GetTerminalSize(JObject input)
{
    foreach (var property in input.Properties())
    {
        if (property.Name == "terminalSize")
            return property.Value.Value<double>();

        if (property.Value.Type == JTokenType.Object)
            return GetTerminalSize((JObject) property.Value);

        //not sure, if the is a need to handle an array
        if (property.Value.Type == JTokenType.Array)
            foreach (var item in (JArray) property.Value)
                return GetTerminalSize((JObject) item);
    }

    return 0;
}

It returns a correct value for all 3 examples

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anu Viswan
Solution 2
Solution 3