AGI_rev
AGI_rev

Reputation: 131

Is there a simple way of handling nested Dictionary<string, object> types where object is either string or another Dictionary<string, object>?

I'm creating a method that should be able to have a Dictionary as parameter.

This method prepares a set of parameters to be appended to a URI as get/post parameters. The problem is that when I call BuildQueryData(item), I get an error: Cannot convert KeyValuePair to Dictionary.

    private string BuildQueryData(Dictionary<string, object> param)
    {
        if (param == null)
            return "";

        StringBuilder b = new StringBuilder();
        foreach (var item in param)
        {
            Dictionary<string, object> o;
            if (item.GetType() == "".GetType())
                b.Append(string.Format("&{0}={1}", item.Key, WebUtility.UrlEncode((string)item.Value)));
            else if (item.GetType() == new Dictionary<string, object>().GetType())
                b.Append(BuildQueryData(item));
        }


        try { return b.ToString().Substring(1); }
        catch (Exception e)
        {
            log.Error(e.Message);
            return e.Message;
        }
    }

Depending on the type of the object passed inside the Dictionary, it should either create a string when a string is passed or call itself when another Dictionary is passed.

Thanks in advance for your expertise

Upvotes: 1

Views: 218

Answers (2)

Theodor Zoulias
Theodor Zoulias

Reputation: 44026

You could make it more fancy by creating a method that flattens a nested dictionary:

private static IEnumerable<KeyValuePair<string, string>>
    Flatten(Dictionary<string, object> dictionary)
{
    if (dictionary == null) yield break;
    foreach (var entry in dictionary)
    {
        if (entry.Value is string s)
        {
            yield return new KeyValuePair<string, string>(entry.Key, s);
        }
        else if (entry.Value is Dictionary<string, object> innerDictionary)
        {
            foreach (var innerEntry in Flatten(innerDictionary))
            {
                yield return innerEntry;
            }
        }
        else if (entry.Value == null)
        {
            // Do nothing
        }
        else
        {
            throw new ArgumentException(nameof(dictionary));
        }
    }
}

...and then use it like this:

string queryData = String.Join("&", Flatten(myNestedDictionary)
    .Select(e => e.Key + "=" + WebUtility.UrlEncode(e.Value)));

Upvotes: 1

Stephan Bauer
Stephan Bauer

Reputation: 9249

You have to check item.Value instead of item. Furthermore I tried to improve your code a little bit by using is instead of GetType() and getting rid of the try-catch here. (And why Substring(1)?)
(Somehow I got a feeling that the suggested approach with overloading won't work that easily in this case, but I'm not sure about this.)

private string BuildQueryData(Dictionary<string, object> param)
{
    if (param == null )
        return "";

    StringBuilder b = new StringBuilder();
    foreach (var item in param)
    {
        if (item.Value is string s)
            b.AppendFormat("&{0}={1}", item.Key, WebUtility.UrlEncode(s));
        else if (item.Value is Dictionary<string, object> dict)
            b.Append(BuildQueryData(dict));
    }

    return b.ToString();
}

Upvotes: 1

Related Questions