POST json字典
我正在尝试以下方法:带有字典的模型在第一个Ajax请求上发送它,然后将结果再次序列化并将其发送回控制器。
这应该testing我可以在我的模型中找回字典。 它不起作用
这是我的简单testing:
public class HomeController : Controller { public ActionResult Index (T a) { return View(); } public JsonResult A(T t) { if (t.Name.IsEmpty()) { t = new T(); t.Name = "myname"; tD = new Dictionary<string, string>(); tDAdd("a", "a"); tDAdd("b", "b"); tDAdd("c", "c"); } return Json(t); } } //model public class T { public string Name { get; set; } public IDictionary<string,string> D { get; set; } }
javascript:
$(function () { var o = { Name: 'somename', "D": { "a": "b", "b": "c", "c": "d" } }; $.ajax({ url: actionUrl('/home/a'), contentType: 'application/json', type: 'POST', success: function (result) { $.ajax({ url: actionUrl('/home/a'), data: JSON.stringify(result), contentType: 'application/json', type: 'POST', success: function (result) { } }); } }); });
在萤火虫json收到和json发送是相同的。 我只能假设路上会有东西丢失。
任何人有一个想法,我做错了什么?
由于JsonValueProviderFactory实现的方式,不支持绑定字典。
一个不幸的解决方法:
data.dictionary = { 'A': 'a', 'B': 'b' }; data.dictionary = JSON.stringify(data.dictionary); . . . postJson('/mvcDictionaryTest', data, function(r) { debugger; }, function(a,b,c) { debugger; });
postJSON js lib函数(使用jQuery):
function postJson(url, data, success, error) { $.ajax({ url: url, data: JSON.stringify(data), type: 'POST', contentType: 'application/json; charset=utf-8', dataType: 'json', success: success, error: error }); }
被发布的ViewModel对象(大概比字典还要多):
public class TestViewModel { . . . //public Dictionary<string, string> dictionary { get; set; } public string dictionary { get; set; } . . . }
Controller方法发布到:
[HttpPost] public ActionResult Index(TestViewModel model) { var ser = new System.Web.Script.Serialization.JavascriptSerializer(); Dictionary<string, string> dictionary = ser.Deserialize<Dictionary<string, string>>(model.dictionary); // Do something with the dictionary }
我今天遇到同样的问题,想出了一个解决scheme,不需要任何东西,只需注册一个新的模型绑定器。 这有点哈克,但希望它可以帮助某人。
public class DictionaryModelBinder : IModelBinder { /// <summary> /// Binds the model to a value by using the specified controller context and binding context. /// </summary> /// <returns> /// The bound value. /// </returns> /// <param name="controllerContext">The controller context.</param><param name="bindingContext">The binding context.</param> public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException("bindingContext"); string modelName = bindingContext.ModelName; // Create a dictionary to hold the results IDictionary<string, string> result = new Dictionary<string, string>(); // The ValueProvider property is of type IValueProvider, but it typically holds an object of type ValueProviderCollect // which is a collection of all the registered value providers. var providers = bindingContext.ValueProvider as ValueProviderCollection; if (providers != null) { // The DictionaryValueProvider is the once which contains the json values; unfortunately the ChildActionValueProvider and // RouteDataValueProvider extend DictionaryValueProvider too, so we have to get the provider which contains the // modelName as a key. var dictionaryValueProvider = providers .OfType<DictionaryValueProvider<object>>() .FirstOrDefault(vp => vp.ContainsPrefix(modelName)); if (dictionaryValueProvider != null) { // There's no public property for getting the collection of keys in a value provider. There is however // a private field we can access with a bit of reflection. var prefixsFieldInfo = dictionaryValueProvider.GetType().GetField("_prefixes", BindingFlags.Instance | BindingFlags.NonPublic); if (prefixsFieldInfo != null) { var prefixes = prefixsFieldInfo.GetValue(dictionaryValueProvider) as HashSet<string>; if (prefixes != null) { // Find all the keys which start with the model name. If the model name is model.DictionaryProperty; // the keys we're looking for are model.DictionaryProperty.KeyName. var keys = prefixes.Where(p => p.StartsWith(modelName + ".")); foreach (var key in keys) { // With each key, we can extract the value from the value provider. When adding to the dictionary we want to strip // out the modelName prefix. (+1 for the extra '.') result.Add(key.Substring(modelName.Length + 1), bindingContext.ValueProvider.GetValue(key).AttemptedValue); } return result; } } } } return null; } }
活页夹在application_start下的Global.asax文件中注册
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.Add(typeof(Dictionary<string,string>), new DictionaryModelBinder()); }
我得到它与一个自定义模型联编程序,并改变数据的发送方式; 而不使用Stringify并设置contenttype。
JavaScript的:
$(function() { $.ajax({ url: '/home/a', type: 'POST', success: function(result) { $.ajax({ url: '/home/a', data: result, type: 'POST', success: function(result) { } }); } }); });
自定义模型绑定器
public class DictionaryModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException("bindingContext"); string modelName = bindingContext.ModelName; IDictionary<string, string> formDictionary = new Dictionary<string, string>(); Regex dictionaryRegex = new Regex(modelName + @"\[(?<key>.+?)\]", RegexOptions.CultureInvariant); foreach (var key in controllerContext.HttpContext.Request.Form.AllKeys.Where(k => k.StartsWith(modelName + "["))) { Match m = dictionaryRegex.Match(key); if (m.Success) { formDictionary[m.Groups["key"].Value] = controllerContext.HttpContext.Request.Form[key]; } } return formDictionary; } }
通过在Global.asax中添加模型联编程序:
ModelBinders.Binders[typeof(IDictionary<string, string>)] = new DictionaryModelBinder();
为System.Json
以下NuGet包,其中包含新的JsonValue
types。 JsonValue
是一种灵活的新型JSON代表types,完全支持C#4dynamic,并且在希望将有效内容视为字典/关联数组的情况下,它也是一个IEnumerable<KeyValuePair<string, JsonValue>>
。
你可以在这里使用NuGet来获取System.Json(Beta) 。 看来, System.Json
将被本地包含在.NET 4.5中,正如文档页面所示 。
您可能还需要阅读以下文章,以帮助获取JSON HTTP正文以正确反序列化到Action方法参数中的JsonValue对象:
JSON,ASP.NET MVC和JQuery – 使用非types化的JSON变得简单
上面这篇文章中的两个相关代码片段是DynamicJsonBinder和DynamicJsonAttribute,在这里为后代而复制:
public class DynamicJsonBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (!controllerContext.HttpContext.Request.ContentType.StartsWith ("application/json", StringComparison.OrdinalIgnoreCase)) { // not JSON request return null; } var inpStream = controllerContext.HttpContext.Request.InputStream; inpStream.Seek(0, SeekOrigin.Begin); StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); string bodyText = reader.ReadToEnd(); reader.Close(); if (String.IsNullOrEmpty(bodyText)) { // no JSON data return null; } return JsonValue.Parse(bodyText); } } public class DynamicJsonAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new DynamicJsonBinder(); } }
一个相关的示例用例是:
public class HomeController : Controller { public ActionResult Index (T a) { return View(); } public JsonResult A([DynamicJson] JsonValue value) { dynamic t = value.AsDynamic(); if (t.Name.IsEmpty()) { t = new // t is dynamic, so I figure just create the structure you need directly { Name = "myname", D = new // Associative array notation (woot!): { a = "a", b = "b", c = "c" } }; } return Json(t); } }
只要使用一个更好的解串器。 我设置位置的第一行是因为JsonValueProvider最后离开了stream。 更多MS JSON失败。
Request.InputStream.Position = 0; var reader = new StreamReader(Request.InputStream); var model = Newtonsoft.Json.JsonConvert.DeserializeObject<CreativeUploadModel>(reader.ReadToEnd());
所以在CreativeUploadModel对象图中的某个地方有一个像这样的道具:
public Dictionary<string, Asset> Assets { get; set; }
从(例如)反序列化的:
"assets":{"flash":{"type":"flash","value":"http://1234.cloudfront.net/1234.swf","properties":"{\"clickTag\":\"clickTAG\"}"}
Newtonsoft JSON是WebAPI的默认JSON提供程序…所以它不会去任何地方。
这是我对类似问题的解决scheme:
using System.Collections.Generic; using System.IO; using System.Web.Mvc; using System.Web.Script.Serialization; namespace Controllers { public class DictionaryModelBinder : IModelBinder { public object BindModel(ControllerContext context, ModelBindingContext bindingContext) { context.HttpContext.Request.InputStream.Seek(0, SeekOrigin.Begin); using (TextReader reader = new StreamReader(context.HttpContext.Request.InputStream)) { string requestContent = reader.ReadToEnd(); var arguments = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(requestContent); return arguments[bindingContext.ModelName]; } } } } using Controllers; using Moq; using NUnit.Framework; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Web; using System.Web.Mvc; namespace ControllersTest { [TestFixture] public class DictionaryModelBinderTest { private ControllerContext controllerContext; [Test] public void ReturnsDeserializedPrimitiveObjectsAndDictionaries() { string input = @"{ arguments: { simple: 1, complex: { a: 2, b: 3 }, arrayOfSimple: [{ a: 4, b: 5 }], arrayOfComplex: [{ a: 6, b: 7 }, { a: 8, b: 9 }]}, otherArgument: 1 }"; SetUpRequestContent(input); var binder = new DictionaryModelBinder(); var bindingContext = new ModelBindingContext(); bindingContext.ModelName = "arguments"; var model = (Dictionary<string, object>)binder.BindModel(controllerContext, bindingContext); Assert.IsFalse(model.ContainsKey("otherArgument")); Assert.AreEqual(1, model["simple"]); var complex = (Dictionary<string, object>)model["complex"]; Assert.AreEqual(2, complex["a"]); Assert.AreEqual(3, complex["b"]); var arrayOfSimple = (ArrayList)model["arrayOfSimple"]; Assert.AreEqual(4, ((Dictionary<string, object>)arrayOfSimple[0])["a"]); Assert.AreEqual(5, ((Dictionary<string, object>)arrayOfSimple[0])["b"]); var arrayOfComplex = (ArrayList)model["arrayOfComplex"]; var complex1 = (Dictionary<string, object>)arrayOfComplex[0]; var complex2 = (Dictionary<string, object>)arrayOfComplex[1]; Assert.AreEqual(6, complex1["a"]); Assert.AreEqual(7, complex1["b"]); Assert.AreEqual(8, complex2["a"]); Assert.AreEqual(9, complex2["b"]); } private void SetUpRequestContent(string input) { var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); stream.Seek(0, SeekOrigin.End); var controllerContextStub = new Mock<ControllerContext>(); var httpContext = new Mock<HttpContextBase>(); httpContext.Setup(x => x.Request.InputStream).Returns(stream); controllerContextStub.Setup(x => x.HttpContext).Returns(httpContext.Object); this.controllerContext = controllerContextStub.Object; } } } using Controllers; using PortalApi.App_Start; using System.Collections.Generic; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; namespace PortalApi { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); RouteConfig.RegisterRoutes(RouteTable.Routes); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); ModelBinders.Binders.Add(typeof(Dictionary<string, object>), new DictionaryModelBinder()); } } }
玩的开心! :-P问候Łukasz杜达
将复杂对象作为string发布,并在另一端反序列化。 然而,这没有types安全。 这是一个包含string键和string数组值的字典。
JS:
var data = { 'dictionary': JSON.stringify({'A': ['a', 'b'] }) }; $.ajax({ url: '/Controller/MyAction', data: JSON.stringify(data), type: 'POST', contentType: 'application/json', dataType: 'json' });
c#控制器:
[HttpPost] public ActionResult MyAction(string dictionary) { var s = new System.Web.Script.Serialization.JavaScriptSerializer(); Dictionary<string, string[]> d = s.Deserialize<Dictionary<string, string[]>>(dictionary); return View(); }
直接使用ASP.NET 5和MVC 6我正在这样做:
JSON:
{ "Name": "somename", "D": { "a": "b", "b": "c", "c": "d" } }
控制器:
[HttpPost] public void Post([FromBody]Dictionary<string, object> dictionary) { }
这是什么时候显示出来(名称和D是关键):
对于最近出现这个问题的人来说,只要你不需要你的控制器来专门接受一本字典,你可以执行以下操作:
HttpResponseMessage SomeMethod([FromBody] IEnumerable<KeyValuePair<Key, Value>> values) { Dictionary<Key, Value> dictionary = values.ToDictionary(x => x.Key, x = x.Value); }
虽然有点哈克。