[ACCEPTED]-How do I pass a Dictionary as a parameter to an ActionResult method from jQuery/Ajax?-actionresult

Accepted answer
Score: 10

At last I figured it out!! Thanks for the 17 suggestions everyone! I finally figured 16 out the best solution is to pass JSON via 15 the Http Post and use a custom ModelBinder 14 to convert the JSON to a Dictionary. One 13 thing I did in my solution is created a 12 JsonDictionary object that inherits from 11 Dictionary so that I can attach the custom 10 ModelBinder to the JsonDictionary type, and 9 it wont cause any conflicts in the future 8 if I use Dictionary as a ActionResult parameter 7 later on for a different purpose than JSON.

Here's 6 the final ActionResult method:

public ActionResult AddItems([Bind(Include="values")] JsonDictionary values)
{
    // do something
}

And the jQuery 5 "$.post" call:

$.post("/Controller/AddItems",
{
    values: Sys.Serialization.JavaScriptSerializer.serialize(
            {
                id: 200,
                "name": "Chris"
            }
        )
},
function(data) { },
"json");

Then the JsonDictionaryModelBinder 4 needs to be registered, I added this to 3 the Application_Start method within the 2 Global.asax.cs:

protected void Application_Start()
{
    ModelBinders.Binders.Add(typeof(JsonDictionary), new JsonDictionaryModelBinder());
}

And, finally here's the JsonDictionaryModelBinder 1 object and JsonDictionary object I created:

public class JsonDictionary : Dictionary<string, object>
{
    public JsonDictionary() { }

    public void Add(JsonDictionary jsonDictionary)
    {
        if (jsonDictionary != null)
        {
            foreach (var k in jsonDictionary.Keys)
            {
                this.Add(k, jsonDictionary[k]);
            }
        }
    }
}

public class JsonDictionaryModelBinder : IModelBinder
{
    #region IModelBinder Members

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.Model == null) { bindingContext.Model = new JsonDictionary(); }
        var model = bindingContext.Model as JsonDictionary;

        if (bindingContext.ModelType == typeof(JsonDictionary))
        {
            // Deserialize each form/querystring item specified in the "includeProperties"
            // parameter that was passed to the "UpdateModel" method call

            // Check/Add Form Collection
            this.addRequestValues(
                model,
                controllerContext.RequestContext.HttpContext.Request.Form,
                controllerContext, bindingContext);

            // Check/Add QueryString Collection
            this.addRequestValues(
                model,
                controllerContext.RequestContext.HttpContext.Request.QueryString,
                controllerContext, bindingContext);
        }

        return model;
    }

    #endregion

    private void addRequestValues(JsonDictionary model, NameValueCollection nameValueCollection, ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        foreach (string key in nameValueCollection.Keys)
        {
            if (bindingContext.PropertyFilter(key))
            {
                var jsonText = nameValueCollection[key];
                var newModel = deserializeJson(jsonText);
                // Add the new JSON key/value pairs to the Model
                model.Add(newModel);
            }
        }
    }

    private JsonDictionary deserializeJson(string json)
    {
        // Must Reference "System.Web.Extensions" in order to use the JavaScriptSerializer
        var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        return serializer.Deserialize<JsonDictionary>(json);
    }
}
Score: 4

This is what I tried. Saves a lot of work. Javascript:

  var dict = {};       
        dict["id"] = "200";
        dict["FirstName"] = "Chris";
        dict["DynamicItem1"] = "Some Value";
        dict["DynamicItem2"] = "Some Other Value";

        var theObject = {};
        theObject.dict = dict;
        $.post(URL, theObject, function (data, textStatus, XMLHttpRequest) {
            console.log("success");
        }, "json");

Action 1 Method:

public ActionResult MethodName(DictionaryModel obj)
    {
       //Action method logic
    }

public class DictionaryModel
{
    public Dictionary<string, string> dict { get; set; }

}
Score: 1

It's possible with custom model binders 5 or filters. Behind the scenes - you will 4 have to do it manually anyway (Request.Form, parse 3 strings, create dictionary tralala), but 2 at least - your controller will be clean 1 and code will be reusable for another actions.

Score: 1

I don't think it's possible to pass in a 19 Dictionary from jQuery/Ajax to an ActionResult 18 method via an Http Post. One thing I figured 17 out that seems to be the easiest to work 16 with is to pass in a JSON object and then 15 parse that out into a Dictionary.

Here's 14 the modified version of of the above calling 13 "$.post" from jQuery that sends JSON as 12 a pseudo-Dictionary:

$.post("/Controller/AddItems",
    {
        values: Sys.Serialization.JavaScriptSerializer.serialize(
                {
                    id: 200,
                    "name": "Chris"
                }
            )
    },
    function(data) { },
    "json");

The "Sys.Serialization.JavaScriptSerializer.serialize" function 11 is a method of the ASP.NET AJAX JavaScript 10 library.

Here's the modified version of the 9 above ActionResult method:

public ActionResult AddItems(Dictionary<string, object> values)
{
    // Must Reference "System.Web.Extensions" in order to use the JavaScriptSerializer
    var json = new System.Web.Script.Serialization.JavaScriptSerializer();
    var data = json.Deserialize<Dictionary<string, string>>(routeValues);

    // do something
}

I think this makes 8 it much easier to Unit Test by passing JSON, instead 7 of using the Form Collection to send/retrieve 6 the collection of key/value pairs. Also, it's 5 easier to get working than figuring out 4 how to build a custom IModelBinder, and 3 a custom IModelBinder might cause issues 2 with other ActionResult methods when this 1 is the only one I need to do this.

Score: 0

DefaultModelBinder is able to bind your 4 POST to array or dictionary. For example:

for 3 arrays:

public ActionResult AddItems(string[] values)

$.post("/Controller/AddItems", { values: "values[0]=200&values[1]=300" },
    function(data) { }, "json");

or:

$.post("/Controller/AddItems", { values: "values=200&values=300" },
    function(data) { }, "json");

for dictionaries:

public ActionResult AddItems(Dictionary<string, object> values)

$.post("/Controller/AddItems", {
    values: "values[0].Key=value0&values[0].Value=200&values[1].Key=value1&values[1].Value=300" }, function(data) { }, "json");

UPDATED:

If your values 2 are in HTML inputs then in jQuery you can 1 do something like this:

var postData = $('input#id1, input#id2, ..., input#idN").serialize();
// or
var postData = $('input.classOfYourInputs").serialize();

$.post("/Controller/AddItems", { values: postData }, function(data) { }, "json");

UPDATED:

Also check this: Scott Hanselman's ComputerZen.com - ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

Score: 0

This is an old post but I can't help having 15 a few remarks anyway.

@eu-ge-ne: "DefaultModelBinder 14 is able to bind your POST to array or dictionary." True 13 but at least for dictionaries I find the 12 required form notation rather counterintuitive.

@Chris: Yesterday 11 I had exactly the same problem while trying 10 to post a JavaScript (JSON) dictionary to 9 a controller action method. I worked out 8 a totally different custom model binder 7 that processes generic dictionaries with 6 different type arguments. I have only tested 5 it in MVC 3 and probably had the advantage 4 of an improved framework.

For the details 3 of my experiences and the source code of 2 the custom model binder, please see my blog 1 post at http://buildingwebapps.blogspot.com/2012/01/passing-javascript-json-dictionary-to.html

More Related questions