10

I need to make a GET request to a method that contains Dictionary as a parameter. I browse through but could not find any kinds of information about how I could send Dictionary so my request hit to my method. Method signature is as like as below

public void AddItems(Dictionary<string,object> Items)

Best Regards,

Kemal

kkocabiyik
  • 4,246
  • 7
  • 30
  • 40
  • 2
    @kkcocabiyik Were you able to pass the Items into your GET request as URL query parameters? If so, can you post a sample of the syntax to use? Thanks! – msplants Oct 08 '13 at 18:19
  • To my knowledge, a GET request with ASP.NET Web API is not able to natively bind a Dictionary. You will have to implement a model binder (https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding) – Dude Pascalou May 03 '22 at 07:35

4 Answers4

11

Did you read ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

Kirill Bestemyanov
  • 11,946
  • 2
  • 24
  • 38
  • Asp. Net Web Api is a part of asp.net mvc – Kirill Bestemyanov Aug 14 '12 at 11:02
  • 3
    @KirillBestemyanov From a marketing perspective yes. From an implementation perspective there is some overlap, however there is also plenty of Web API code that is distinct from MVC. In the case of the Model binder however, I do believe the code is shared. – Darrel Miller Aug 14 '12 at 14:05
  • 1
    @KirillBestemyanov this is not true. As Darrel says it might be marketed as something that's part of it, but that's far from truth. Web API is, first and foremost, stand alone. And model binding itself is much different, see: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx – Filip W Aug 14 '12 at 19:53
  • 2
    This answer is not helpful and quite misleading as the question pertains to Web API, which works differently to MVC 5. – Lunster Apr 12 '16 at 08:50
  • 1
    And may be you add right answer instead of this? This answer was written 1 and half year ago based on mvc 4 and web api 1.0. – Kirill Bestemyanov Apr 12 '16 at 14:36
2

I wrote a ModelBinder that does exactly what you wanted:

public class DictionaryModelBinder : DefaultModelBinder
{
    private const string _dateTimeFormat = "dd/MM/yyyy HH:mm:ss";

    private enum StateMachine
    {
        NewSection,
        Key,
        Delimiter,
        Value,
        ValueArray
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var stream = controllerContext.HttpContext.Request.InputStream;
        string text;

        stream.Position = 0;
        using (var reader = new StreamReader(stream))
        {
            text = reader.ReadToEnd();
        }

        int index = 0;
        return Build(text, ref index);
    }

    private static Dictionary<string, object> Build(string text, ref int index)
    {
        var state = StateMachine.NewSection;
        var dictionary = new Dictionary<string, object>();
        var key = string.Empty;
        object value = string.Empty;

        for (; index < text.Length; ++index)
        {
            if (state == StateMachine.NewSection && text[index] == '{')
            {
                dictionary = new Dictionary<string, object>();
                state = StateMachine.NewSection;
            }
            else if (state == StateMachine.NewSection && text[index] == '"')
            {
                key = string.Empty;
                state = StateMachine.Key;
            }
            else if (state == StateMachine.Key && text[index] != '"')
            {
                key += text[index];
            }
            else if (state == StateMachine.Key && text[index] == '"')
            {
                state = StateMachine.Delimiter;
            }
            else if (state == StateMachine.Delimiter && text[index] == ':')
            {
                state = StateMachine.Value;
                value = string.Empty;
            }
            else if (state == StateMachine.Value && text[index] == '[')
            {
                state = StateMachine.ValueArray;
                value = value.ToString() + text[index];
            }
            else if (state == StateMachine.ValueArray && text[index] == ']')
            {
                state = StateMachine.Value;
                value = value.ToString() + text[index];
            }
            else if (state == StateMachine.Value && text[index] == '{')
            {
                value = Build(text, ref index);
            }
            else if (state == StateMachine.Value && text[index] == ',')
            {
                dictionary.Add(key, ConvertValue(value));
                state = StateMachine.NewSection;
            }
            else if (state == StateMachine.Value && text[index] == '}')
            {
                dictionary.Add(key, ConvertValue(value));
                return dictionary;
            }
            else if (state == StateMachine.Value || state == StateMachine.ValueArray)
            {
                value = value.ToString() + text[index];
            }
        }

        return dictionary;
    }

    private static object ConvertValue(object value)
    {
        string valueStr;
        if (value is Dictionary<string, object> || value == null || (valueStr = value.ToString()).Length == 0)
        {
            return value;
        }

        bool boolValue;
        if (bool.TryParse(valueStr, out boolValue))
        {
            return boolValue;
        }

        int intValue;
        if (int.TryParse(valueStr, out intValue))
        {
            return intValue;
        }

        double doubleValue;
        if (double.TryParse(valueStr, out doubleValue))
        {
            return doubleValue;
        }

        valueStr = valueStr.Trim('"');

        DateTime datetimeValue;
        if (DateTime.TryParseExact(valueStr, _dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out datetimeValue))
        {
            return datetimeValue;
        }

        if (valueStr.First() == '[' && valueStr.Last() == ']')
        {
            valueStr = valueStr.Trim('[', ']');
            if (valueStr.Length > 0)
            {
                if (valueStr[0] == '"')
                {
                    return valueStr
                        .Split(new[] { '"' }, StringSplitOptions.RemoveEmptyEntries)
                        .Where(x => x != ",")
                        .ToArray();
                }
                else
                {
                    return valueStr
                        .Split(',')
                        .Select(x => ConvertValue(x.Trim()))
                        .ToArray();
                }
            }
        }

        return valueStr;
    }
}

More explanations and full post you can see in my blog:

Json To Dictionary generic model binder

Amir Yonatan
  • 717
  • 1
  • 7
  • 14
0

If you have problems receiving Dictionary in your webApi controller, relatively painless solution is to switch the parameter to List<"ObjectRepresentingDict"> insetead. It will map automatically.

Cubelaster
  • 338
  • 4
  • 6
-5

You Can Use Dictionary As A Parameter In This Way:

protected object DictionaryFunction()
{
    Dictionary<int,YourObjectName> YourDictionaryObjectName=new Dictionary<int,YourObjectName>();
    ...
    ...

    return YourDictionaryObjectName;
}

protected MyFunction()
{
    Dictionary<int,YourObjectName> MyDictionary=(Dictionary<int,YourObjectName>)DictionaryFunction();
}
SztupY
  • 10,291
  • 8
  • 64
  • 87
Kunal
  • 1