3

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"
}
1
  • Is terminalSize present only once? Commented Apr 27, 2020 at 21:34

3 Answers 3

6

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;
Sign up to request clarification or add additional context in comments.

Comments

6

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

1 Comment

Thanks for the fiddle links and worked examples. Very helpful!
2

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

1 Comment

Thank you, very helpful

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.