Iterate through a JsonDocument in a recursive way
Asked Answered
S

3

6

I need to iterate through an JsonDocument and perform some sort of check depending on the JsonValueKind I encounter.

I tried to do this validation check in such manner:

public bool Dec(JsonElement Element)
{
    var ElementEnumeratable = Element.EnumerateObject();

    foreach (var Elm in ElementEnumeratable )
    {
        string name = Elm.Name;
        switch (Elm.Value.ValueKind)
        {
            case JsonValueKind.Array:
                var jArray = Elm.Value;
                return Dec(jArray);
            case JsonValueKind.String:
                string jString = Elm.Value.GetString();
                break;
            case JsonValueKind.Number:
                int jNumber = Elm.Value.GetInt32();
                break;
        }
    }

    return true;
}

Problem here is when Elm has ValueKind array - I cannot pass it to the Dec it seems like JsonElement which has valuekind as array, cannot be converted into an EnumerateObject?

What to do here?

Styracaceous answered 3/4, 2020 at 8:56 Comment(3)
What do you want to achieve here? Your Decmethod needs JsonElement type of parameter but you are sending Array.Justajustemilieu
I want do some validation on string and numbers but sometime I get an array => In which case I will need to loop through it and perform checks on string and numbers...Styracaceous
@Styracaceous - My answer to your previous question showed an example of this.Vaughan
J
7

As I understand, if ValueKind is JsonValueKind.Array then you need to iterate array by using EnumerateArray() so that you will have JsonElement and call Dec foreach element to validate each of them, like this:

public static bool Dec(JsonElement Element)
{

    var ElementEnumeratable = Element.EnumerateObject();

    foreach (var Elm in ElementEnumeratable)
    {
        string name = Elm.Name;
        switch (Elm.Value.ValueKind)
        {
            case JsonValueKind.Array:
                var jArray = Elm.Value;
                foreach (var item in jArray.EnumerateArray())
                {
                    Dec(item);
                }
                break;
            case JsonValueKind.String:
                string jString = Elm.Value.GetString();
                break;
            case JsonValueKind.Number:
                int jNumber = Elm.Value.GetInt32();
                break;
        }
    }

    return true;
}
Justajustemilieu answered 3/4, 2020 at 10:49 Comment(1)
This sample code would beak if you had arrays within arrays (because subsequent recursive call will throw on EnumerateObject). The implementation of Dec should check up front whether it is an object or array and call the right enumerator based on the ValueKind rather than assuming that the input is always a JSON object.Oni
F
2

I updated the proposed code this way:

    public static JsonData Decode(JsonElement jsonElement, string elementName = "Root", JsonData parent = null)
    {
        JsonData result = null;
        string jsonString = null;
        double jsonDouble = 0;
        bool jsonBool = false;
        JsonElement jsonArray;

        result = new JsonData(elementName, parent);

        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.Undefined:
                break;

            case JsonValueKind.Object:
                ObjectEnumerator objectEnumerator = jsonElement.EnumerateObject();

                foreach (var item in objectEnumerator)
                {
                    string name = item.Name;
                    switch (item.Value.ValueKind)
                    {
                        case JsonValueKind.Undefined:
                            break;

                        case JsonValueKind.Object:
                            JsonElement element = (JsonElement)item.Value;
                            result.Childrens.Add(Decode(element, name, result));
                            break;

                        case JsonValueKind.Array:
                            jsonArray = item.Value;
                            foreach (var arrayItem in jsonArray.EnumerateArray())
                            {
                                result.Childrens.Add(Decode(arrayItem, name, result));
                            }
                            break;

                        case JsonValueKind.String:
                            jsonString = item.Value.GetString();
                            result.Items.Add(name, jsonString);
                            break;

                        case JsonValueKind.Number:
                            jsonDouble = item.Value.GetDouble();
                            result.Items.Add(name, jsonDouble);
                            break;

                        case JsonValueKind.True:
                            jsonBool = item.Value.GetBoolean();
                            result.Items.Add(name, jsonBool);
                            break;

                        case JsonValueKind.False:
                            jsonBool = item.Value.GetBoolean();
                            result.Items.Add(name, jsonBool);
                            break;

                        case JsonValueKind.Null:
                            result.Items.Add(name, null);
                            break;

                        default:
                            break;
                    }
                }
                break;

            case JsonValueKind.Array:
                jsonArray = jsonElement;
                foreach (var arrayItem in jsonArray.EnumerateArray())
                {
                    result.Items.Add(elementName, arrayItem);
                }
                break;

            case JsonValueKind.String:
                jsonString = jsonElement.GetString();
                result.Items.Add(elementName, jsonString);

                break;
            case JsonValueKind.Number:
                jsonDouble = jsonElement.GetDouble();
                result.Items.Add(elementName, jsonDouble);
                break;

            case JsonValueKind.True:
                jsonBool = jsonElement.GetBoolean();
                result.Items.Add(elementName, jsonBool);
                break;

            case JsonValueKind.False:
                jsonBool = jsonElement.GetBoolean();
                result.Items.Add(elementName, jsonBool);
                break;

            case JsonValueKind.Null:
                result.Items.Add(elementName, null);
                break;

            default:
                break;
        }

        return result;
    }

Returned class is the following one:

public class JsonData
{
    public JsonData Parent { get; private set; }
    public List<JsonData> Childrens { get; private set; }
    public string Name { get; private set; }
    public Dictionary<string, object> Items { get; private set; }

    public JsonData(string name, JsonData parent = null)
    {
        Parent = parent;

        Childrens = new List<JsonData>();

        Name = name;

        Items = new Dictionary<string, object>();
    }

}

Usage:

        JsonDocument jsonDoc = null;
        JsonData jsonData = null;

        string json = File.ReadAllText(@"c:\jsonpath.json");

        if (!string.IsNullOrEmpty(json))
        {
            jsonDoc = JsonDocument.Parse(json, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip });

            if (jsonDoc != null)
            {
                jsonData = Decode(jsonDoc.RootElement);
            }
        }
        
        if (jsonData != null)
        {
            JsonData financialData = jsonData.Childrens.FirstOrDefault(c => c.Name == "Financial Data");

            if (financialData != null)
            {
                List<JsonData> transactions = financialData.Childrens.Where(c => c.Name == "Transaction history").ToList<JsonData>();
            }
        }
Foreword answered 21/5, 2022 at 12:59 Comment(0)
P
0

I think you're doing the switch in the wrong place. If Dec() is called with an Array, it will still call EnumerateObject(). It would need to call EnumerateArray() for an array.

Pavla answered 3/4, 2020 at 9:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.