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"
}