red5.c9 red5.c9 - 4 months ago 53
JSON Question

Nested JSON using c# NewtonSoft

Can anyone provide some advice, maybe with a code snippet, using C# and json.net to read some nested json?
My json below validates to good json using JSONLint.
I've hit a hurdle that when parsing the json, I am unable to get into the 3rd, 4th, 6th and 8th values nested in the root.

[{
"interactionType": "MathEditorInteraction",
"interactionId": "08506178-22ba-4fa7-a490-c785716f10dc",
"value": "blah blah blah"
},
{
"interactionType": "MathEditorInteraction",
"interactionId": "1134871f-980e-4138-9598-0d4bf480aa97",
"value": "my first value"
},
{
"interactionType": "CanvasInteraction",
"interactionId": "89cd7bec-d0e8-4111-8442-f2ab95a1410b",
"value": "my second value"
},
{
"interactionType": "FillInBlankInteraction",
"interactionId": "7e9350b4-fb85-4f12-869e-227f99f77a73",
"value": "{\"results\":[{\"id\":\"1ac6770e-2093-4b7c-b595-789be8ee6efb\",\"value\":\"my third value\"}]}"
},
{
"interactionType": "FillInBlankInteraction",
"interactionId": "6f1ca6b7-3178-44a7-b8e9-e82d8c51d1fd",
"value": "{\"results\":[{\"id\":\"b7e92fd2-9c7a-4f71-88f9-e7d43e3179b7\",\"value\":\"my fourth value\"}]}"
},
{
"interactionType": "TextBoxInteraction",
"interactionId": "284c43f9-a268-4295-b96d-bc2f6dc30f0e",
"value": "my fifth value"
},
{
"interactionType": "FillInBlankInteraction",
"interactionId": "544b9907-139a-4c78-9671-502153be2697",
"value": "{\"results\":[{\"id\":\"f4e1ba6d-61dd-4eed-9c6f-dafc2701c161\",\"value\":\"my sixth value\"}]}"
},
{
"interactionType": "TextBoxInteraction",
"interactionId": "c0a5a1f0-2cae-42fd-8726-0ad36c11f413",
"value": "my seventh value"
},
{
"interactionType": "FillInBlankInteraction",
"interactionId": "ef6a7b62-8a7b-4b7f-b876-0d78ee6c4c87",
"value": "{\"results\":[{\"id\":\"af39469e-c041-4889-9e28-61a438cf56a3\",\"value\":\"my eight value\"}]}"
},
{
"interactionType": "TextBoxInteraction",
"interactionId": "f04de5b5-8a29-4200-a886-15f7dbd575b6",
"value": "my nineth value"
}]


then some c# that I've been using:

JArray token = JArray.Parse(response); // response = json string above
for (int i = 0; i < token.Count; i++)
{
String value = token[i]["value"].ToString();
}


I could be surprised with the JSON i consume...it could have n-count nested values...the purpose of my code is to get to the bottom-most child with the "value" string.
Is there a way to look at a token[i]["some string"] and see if it contains a JArray or JObject to continue parsing?

EDIT from Timothy's suggestion, I was able to output the values. Almost there.

static string json2 = @"[{""interactionType"": ""MathEditorInteraction"",""interactionId"": ""08506178-22ba-4fa7-a490-c785716f10dc"",""value"": ""blah blah blah""},{""interactionType"": ""MathEditorInteraction"",""interactionId"": ""1134871f-980e-4138-9598-0d4bf480aa97"",""value"": ""my first value""},{""interactionType"": ""CanvasInteraction"",""interactionId"": ""89cd7bec-d0e8-4111-8442-f2ab95a1410b"",""value"": ""my second value""},{""interactionType"": ""FillInBlankInteraction"",""interactionId"": ""7e9350b4-fb85-4f12-869e-227f99f77a73"",""value"": ""{\""results\"":[{\""id\"":\""1ac6770e-2093-4b7c-b595-789be8ee6efb\"",\""value\"":\""my third value\""}]}""},{""interactionType"": ""FillInBlankInteraction"",""interactionId"": ""6f1ca6b7-3178-44a7-b8e9-e82d8c51d1fd"",""value"": ""{\""results\"":[{\""id\"":\""b7e92fd2-9c7a-4f71-88f9-e7d43e3179b7\"",\""value\"":\""my fourth value\""}]}""},{""interactionType"": ""TextBoxInteraction"",""interactionId"": ""284c43f9-a268-4295-b96d-bc2f6dc30f0e"",""value"": ""my fifth value""},{""interactionType"": ""FillInBlankInteraction"",""interactionId"": ""544b9907-139a-4c78-9671-502153be2697"",""value"": ""{\""results\"":[{\""id\"":\""f4e1ba6d-61dd-4eed-9c6f-dafc2701c161\"",\""value\"":\""my sixth value\""}]}""},{""interactionType"": ""TextBoxInteraction"",""interactionId"": ""c0a5a1f0-2cae-42fd-8726-0ad36c11f413"",""value"": ""my seventh value""},{""interactionType"": ""FillInBlankInteraction"",""interactionId"": ""ef6a7b62-8a7b-4b7f-b876-0d78ee6c4c87"",""value"": ""{\""results\"":[{\""id\"":\""af39469e-c041-4889-9e28-61a438cf56a3\"",\""value\"":\""my eight value\""}]}""},{""interactionType"": ""TextBoxInteraction"",""interactionId"": ""f04de5b5-8a29-4200-a886-15f7dbd575b6"",""value"": ""my ninth value""}]";

var x = JsonConvert.DeserializeObject<Interaction[]>(json2);
for (int i = 0; i < x.Length; i++)
{
if (x[i].value.Contains("[{"))
{
var v = JsonConvert.DeserializeObject<Nested>(x[i].value);
Console.WriteLine(v.results[0].value);
}
else
{
Console.WriteLine(x[i].value);
}
}


Console Output:

blah blah blah
my first value
my second value
my third value
my fourth value
my fifth value
my sixth value
my seventh value
my eight value
my ninth value


Still stuck on actually detecting an array deeper than the first level.
You can see my hack to look for "[{" which is not preferred.

Answer

You can react to JToken.Type if you don't wish to go into deserializing objects. I actually mostly work with the JSON in a dynamic fasion in C# my self, never deserializing it into strong types, this is mostly because we work with backends where the data structures are defined by the clients and we need to be able to handle much more dynamic data.

https://dotnetfiddle.net/awUSGT

    dynamic arr = JArray.Parse(JSON); // response = json string above

    foreach (dynamic token in arr)
    {
        JTokenType type = ((JToken)token.value).Type;

        switch (type)
        {
            case JTokenType.String:
                Console.WriteLine(token.value);
                break;

            case JTokenType.Object:
                Console.WriteLine(token.value.results.Last.value);
                break;
        }
    }

Note that for before 8.x or 9.x (can't remember when I posted that fix) something the above will thrown an exception when casting to JToken.

So can instead do:

    dynamic arr = JArray.Parse(JSON); // response = json string above

    foreach (JObject token in arr)
    {
        dynamic value = token["value"];
        switch (token["value"].Type)
        {
            case JTokenType.String:
                Console.WriteLine(value);
                break;

            case JTokenType.Object:
                Console.WriteLine(value.results.Last.value);
                break;
        }
    }

Now as for one final note, the tool i used to prettify your JSON seemed to strip away some escaping for your values.

I don't know if it was intentional that you have your embeded JSON as strings (Serialized JSON), if so you need to find a way to discover that and then parse that as well.

A loose/relaxed approach could be:

    dynamic arr = JArray.Parse(JSON); // response = json string above

    foreach (dynamic token in arr)
    {
        string tokenvalue = (string) token.value;
        JToken value = Regex.IsMatch(tokenvalue, "^\\{.*\\}$") 
            ? JToken.Parse(tokenvalue) 
            : token.value;
        switch (value.Type)
        {
            case JTokenType.String:
                Console.WriteLine(value);
                break;

            case JTokenType.Object:
                Console.WriteLine(((dynamic)value).results.Last.value);
                break;
        }
    }

Since I don't know exactly how your "recursive nesting" looks, it is hard to guess, but something along the lines of:

public static void Main()
{
    dynamic arr = JArray.Parse(JSON); // response = json string above

    foreach (dynamic token in arr)
    {
        JToken value = ExtractValue(token);
        Console.WriteLine(value);
    }
}

private static JToken ExtractValue(dynamic token)
{
    string tokenvalue = (string) token.value;
    JToken value = Regex.IsMatch(tokenvalue, "^\\{.*\\}$")
        ? JToken.Parse(tokenvalue)
        : token.value;

    switch (value.Type)
    {
        case JTokenType.String:
            return value;

        case JTokenType.Object:
            return ExtractValue(((dynamic) value).results.Last);

        default:
            throw new InvalidOperationException("Could not extract data, unknown json construct.");
    }
}

Maybe.