C# DefaultModelBinder无法反序列化作为JSON对象传递给操作的.NET字典对象?

C# DefaultModelBinder无法反序列化作为JSON对象传递给操作的.NET字典对象?,c#,json,asp.net-mvc-3,deserialization,defaultmodelbinder,C#,Json,Asp.net Mvc 3,Deserialization,Defaultmodelbinder,我有一个非常简单的课程: public class FilterItem { public Dictionary<string, string> ItemsDictionary { get; set; } public FilterItem() { ItemsDictionary = new Dictionary<string, string>(); } } 以下是我的行动方法的简化版本: [HttpPost] publ

我有一个非常简单的课程:

public class FilterItem
{
    public Dictionary<string, string> ItemsDictionary { get; set; }

    public FilterItem()
    {
        ItemsDictionary = new Dictionary<string, string>();
    }
}
以下是我的行动方法的简化版本:

[HttpPost]
public ActionResult GetFilteredProductsJson(FilterItem filterItem)
{   
    ProductsModel productsModel = new ProductsModel();
    return View("SevenSpikes.Nop.UI.Views.Products", productsModel);
}
请注意,反之亦然。当作为JsonResult传递时,FilterItem对象被成功序列化并作为JSON对象传递给客户端。然而,试图走另一条路是行不通的

我读到并认为这项工作会奏效,但事实并非如此


是否可以在ASP.NET MVC 3中使用DefaultModelBinder反序列化.NET字典?

您是否尝试过以下方法

var simpleDictionary = {"ItemsDictionary": {"1": "5", "2": "7"}};

$.ajax({ cache: false, type: "POST", data: {filterItem : JSON.stringify(simpleDictionary)},
contentType: "application/json; charset=utf-8", 
url: "/Catalog7Spikes/GetFilteredProductsJson", success: function (data) {...});

更新

基于Jeroen的博客帖子(见下面的答案,带有链接),以及我在重新检查代码后的一次大脑闪现,我更新了ExtendedJsonValueProviderFactory,以便它始终能够正确地为通过JSON提交的顶级词典创建备份存储

代码可在GitHub上获得,一个工作示例位于


通过删除当前的
JsonValueProviderFactory
并替换一个可以处理字典创建的工厂,您可以绑定字典。首先,正如Keith指出的,在Javascript中,请确保将字典包装在“filterItem”中,因为这是控制器操作中模型变量的名称,对于JSON,控制器操作中变量的名称必须与返回的JSON元素的名称匹配。此外,在传递类时,任何嵌套元素都必须与类中属性的名称匹配

接下来,创建一个
ExtendedJsonValueProviderFactory
类,如下所示:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Web.Script.Serialization;

public sealed class ExtendedJsonValueProviderFactory : ValueProviderFactory
{

    private void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
    {
        IDictionary<string, object> d = value as IDictionary<string, object>;
        if (d != null)
        {
            foreach (KeyValuePair<string, object> entry in d)
            {
                if (entry.Key.EndsWith("Dictionary", StringComparison.CurrentCulture))
                    CreateDictionary(backingStore, entry);
                else
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
            }
            return;
        }

        IList l = value as IList;
        if (l != null)
        {
            for (int i = 0; i < l.Count; i++)
            {
                AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
            }
            return;
        }

        // primitive
        backingStore[prefix] = value;
    }

    private void CreateDictionary(Dictionary<string, object> backingStore, KeyValuePair<string, object> source)
    {
        var d = source.Value as IDictionary<string, object>;
        var dictionary = new Dictionary<string, string>();
        foreach (KeyValuePair<string, object> entry in d)
            dictionary.Add(entry.Key, entry.Value.ToString());

        AddToBackingStore(backingStore, source.Key, dictionary);
        return;
    }

    private static object GetDeserializedObject(ControllerContext controllerContext)
    {
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
        {
            // not JSON request
            return null;
        }

        StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
        string bodyText = reader.ReadToEnd();
        if (String.IsNullOrEmpty(bodyText))
        {
            // no JSON data
            return null;
        }

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        object jsonData = serializer.DeserializeObject(bodyText);
        return jsonData;
    }

    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        object jsonData = GetDeserializedObject(controllerContext);
        if (jsonData == null)
        {
            return null;
        }

        Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        AddToBackingStore(backingStore, String.Empty, jsonData);

        return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
    }

    private static string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
    }
}
这将删除标准的JsonValueProviderFactory,并用我们的扩展类替换它


最后一步:享受美好。

昨天,我在试图将JavaScript(JSON)字典发布到控制器操作方法时遇到了完全相同的问题。我创建了一个自定义模型绑定器,该绑定器处理具有不同类型参数的通用字典,包括直接(在action方法参数中)或包含在模型类中。我只在MVC3中测试过它


有关我的经验和自定义模型活页夹源代码的详细信息,请参阅我的博客文章,网址为

默认模型活页夹无法处理列表。 我在我的开源项目中解决了这个问题:并写了一篇关于这个问题的文章:阅读这里

…Asp.net MVC具有将发送的数据转换为强类型对象的内置功能。但我们发送的数据必须以正确的方式准备,以便默认数据绑定器可以使用它并填充控制器动作参数对象的属性。 问题是向jQuery.ajax()函数调用提供JSON对象不起作用。完全数据未在服务器上绑定数据,因此控制器操作参数的默认值可能无论如何都是无效的。 问题是jQuery将JSON对象转换为请求查询字符串,而第二级属性值被破坏成Asp.net MVC默认模型绑定器无法理解的形式


Hanselman谈到了这一点:

资料来源:

DefaultModelBinder
希望字典使用一些不太理想的语法。尝试使用这种语法:

 {
 "dictionary[0]":{"Key":"a", "Value":"b"},
 "dictionary[1]":{"Key":"b", "Value":"b"}
 }
它有点笨重,但很结实。下面的工作,以及,但我个人更喜欢以上;它比较短

 {
 "dictionary[0].Key":"a",
 "dictionary[0].Value":"b",
 "dictionary[1].Key":"b"
 "dictionary[1].Value":"b"
 }

这个线程可能会有所帮助-据我所知,.Net不喜欢序列化/反序列化字典。您可能需要将其转换为IEnumerable,然后在字典的构造函数中使用它。它将仅对字典进行反序列化/序列化。
 {
 "dictionary[0]":{"Key":"a", "Value":"b"},
 "dictionary[1]":{"Key":"b", "Value":"b"}
 }
 {
 "dictionary[0].Key":"a",
 "dictionary[0].Value":"b",
 "dictionary[1].Key":"b"
 "dictionary[1].Value":"b"
 }