I have a very simple class:
public class FilterItem
{
public Dictionary<string, string> ItemsDictionary { get; set; }
public FilterItem()
{
ItemsDictionary = new Dictionary<string, string>();
}
}
I want to populate the data in the dictionary on the client and then pass it to my controller action as a JSON object. However no matter what I try on the client, the DefaultModelBinder does not seem to be able to deserialize it.
Here is an example javascript code to call my action:
var simpleDictionary = {"ItemsDictionary": {"1": "5", "2": "7"}};
$.ajax({ cache: false, type: "POST", data: JSON.stringify(simpleDictionary),
contentType: "application/json; charset=utf-8",
url: "/Catalog7Spikes/GetFilteredProductsJson", success: function (data) {...});
And here is a simplified version of my action method:
[HttpPost]
public ActionResult GetFilteredProductsJson(FilterItem filterItem)
{
ProductsModel productsModel = new ProductsModel();
return View("SevenSpikes.Nop.UI.Views.Products", productsModel);
}
Please note that the opposite works. When passed as a JsonResult the FilterItem object is successfully serialized and passed as a JSON object to the client. However trying to go the other way round does not work.
I read the ticket on Connect and thought that the work around would work but it does not.
Is it possible at all to deserialize a .NET dictionary using the DefaultModelBinder in ASP.NET MVC 3?
Hanselman talks about this:
Source: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
The
DefaultModelBinder
expects some less-than-optimal syntax for dictionaries. Try using this kind of syntax:It's kind of bulky but it binds. The following works as well, but I personally prefer the above; it's shorter.
Have you tried the following?
UPDATE
Based upon the blog post by Jeroen (see his answer below, with the link), and a brain flash I had after re-reviewing my code, I have updated the ExtendedJsonValueProviderFactory so that it will always properly create a BackingStore for a top-level dictionary submitted via JSON.
The code is available on GitHub at https://github.com/counsellorben/ASP.NET-MVC-JsonDictionaryBinding, and a working example is at http://oss.form.vu/json-dictionary-example/.
By removing the current
JsonValueProviderFactory
and substituting one which can handle dictionary creation, you can bind your dictionary. First, as Keith pointed out, in your Javascript, be sure to wrap your dictionary inside of "filterItem", since this is the name of the model variable in your controller action, and for JSON, the name of the variable in the controller action must match the name of the Json element being returned. Also, when passing a class, any nested elements must match the names of the properties in the class.Next, create an
ExtendedJsonValueProviderFactory
class, as follows:You may notice that this class is almost identical to the standard JsonValueProviderFactory class, except for the extension to build an entry into the DictionaryValueProvider of type
Dictionary<string,string>
. You also should notice that, in order to be processed as a dictionary, an element must have a name ending in "Dictionary" (and while I think this is a significant code smell, I cannot think of another alternative at this time ... I am open to suggestions).Next, add the following to
Application_Start
inGlobal.asax.cs
:This will remove the standard JsonValueProviderFactory, and replace it with our extended class.
Final step: enjoy the goodness.
Default model binder cannot handle list. I resolved this issue in my open source project: http://jsaction.codeplex.com and wrote an article about this issue: have a read here http://jsaction.codeplex.com/wikipage?title=AllFeatures&referringTitle=Documentation
Yesterday I had exactly the same problem while trying to post a JavaScript (JSON) dictionary to a controller action method. I created a custom model binder that processes generic dictionaries with different type arguments, both directly (in a action method parameter) or contained in a model class. I have only tested it in MVC 3.
For the details of my experiences and the source code of the custom model binder, please see my blog post at http://buildingwebapps.blogspot.com/2012/01/passing-javascript-json-dictionary-to.html