如何在MVC5项目中使用Json.NET进行JSON模型绑定?

我一直在网上寻找答案或例子,但还找不到一个。 我只想更改默认的JSON序列化程序,用于在模型绑定到JSON.NET库时反序列化JSON。

我发现这个 SOpost,但到目前为止无法实现它,我甚至看不到System.Net.Http.Formatters命名空间,也看不到GlobalConfiguration

我错过了什么?

UPDATE

我有一个ASP.NET MVC项目,它基本上是一个MVC3项目。 目前我的目标是.NET 4.5并使用ASP.NET MVC 5和相关的NuGet包。

我没有看到System.Web.Http程序集,也没有看到任何类似的命名空间。 在这种情况下,我想注入JSON.NET作为JSON类型请求的默认模型绑定器。

我终于找到了答案。 基本上我不需要MediaTypeFormatter东西,它不是设计用于MVC环境,而是在ASP.NET Web API中,这就是为什么我没有看到那些引用和命名空间(顺便说一句,那些包含在Microsoft.AspNet.WeApi NuGet包)。

解决方案是使用自定义值提供程序工厂。 这是所需的代码。

  public class JsonNetValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { // first make sure we have a valid context if (controllerContext == null) throw new ArgumentNullException("controllerContext"); // now make sure we are dealing with a json request if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) return null; // get a generic stream reader (get reader for the http stream) var streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream); // convert stream reader to a JSON Text Reader var JSONReader = new JsonTextReader(streamReader); // tell JSON to read if (!JSONReader.Read()) return null; // make a new Json serializer var JSONSerializer = new JsonSerializer(); // add the dyamic object converter to our serializer JSONSerializer.Converters.Add(new ExpandoObjectConverter()); // use JSON.NET to deserialize object to a dynamic (expando) object Object JSONObject; // if we start with a "[", treat this as an array if (JSONReader.TokenType == JsonToken.StartArray) JSONObject = JSONSerializer.Deserialize>(JSONReader); else JSONObject = JSONSerializer.Deserialize(JSONReader); // create a backing store to hold all properties for this deserialization var backingStore = new Dictionary(StringComparer.OrdinalIgnoreCase); // add all properties to this backing store AddToBackingStore(backingStore, String.Empty, JSONObject); // return the object in a dictionary value provider so the MVC understands it return new DictionaryValueProvider(backingStore, CultureInfo.CurrentCulture); } private static void AddToBackingStore(Dictionary backingStore, string prefix, object value) { var d = value as IDictionary; if (d != null) { foreach (var entry in d) { AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value); } return; } var l = value as IList; if (l != null) { for (var i = 0; i < l.Count; i++) { AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]); } return; } // primitive backingStore[prefix] = value; } 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; } } 

你可以在Application_Start方法中使用它:

 // remove default implementation ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType().FirstOrDefault()); // add our custom one ValueProviderFactories.Factories.Add(new JsonNetValueProviderFactory()); 

这是一个向我指出正确方向的post, 这个也给了价值提供者和模型组合者一个很好的解释。

我也有这样的问题。 我将JSON发布到一个动作,但我的JsonProperty名称被忽略了。 因此,我的模型属性始终为空。

 public class MyModel { [JsonProperty(PropertyName = "prop1")] public int Property1 { get; set; } [JsonProperty(PropertyName = "prop2")] public int Property2 { get; set; } [JsonProperty(PropertyName = "prop3")] public int Property3 { get; set; } public int Foo { get; set; } } 

我发布到使用此自定义jquery函数的操作:

 (function ($) { $.postJSON = function (url, data, dataType) { var o = { url: url, type: 'POST', contentType: 'application/json; charset=utf-8' }; if (data !== undefined) o.data = JSON.stringify(data); if (dataType !== undefined) o.dataType = dataType; return $.ajax(o); }; }(jQuery)); 

我称之为:

 data = { prop1: 1, prop2: 2, prop3: 3, foo: 3, }; $.postJSON('/Controller/MyAction', data, 'json') .success(function (response) { ...do whatever with the JSON I got back }); 

不幸的是,只有foo被绑定(奇怪,因为情况不一样,但我想默认的模型绑定器不区分大小写)

 [HttpPost] public JsonNetResult MyAction(MyModel model) { ... } 

解决方案最终变得相当简单

我刚刚实现了Dejan的模型绑定器的通用版本,它对我来说非常好用。 它可能会使用一些虚拟检查(比如确保请求实际上是application / json),但它现在正在做这个技巧。

 internal class JsonNetModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { controllerContext.HttpContext.Request.InputStream.Position = 0; var stream = controllerContext.RequestContext.HttpContext.Request.InputStream; var readStream = new StreamReader(stream, Encoding.UTF8); var json = readStream.ReadToEnd(); return JsonConvert.DeserializeObject(json, bindingContext.ModelType); } } 

当我想在特定的动作上使用它时,我只是告诉它我想要使用我的自定义Json.Net模型绑定器:

 [HttpPost] public JsonNetResult MyAction([ModelBinder(typeof(JsonNetModelBinder))] MyModel model) { ... } 

现在我的[JsonProperty(PropertyName =“”)]属性不再在MyModel上被忽略,一切都被正确绑定!

在我的情况下,我不得不反序列化复杂的对象,包括接口和动态加载的类型等。因此提供自定义值提供程序不起作用,因为MVC仍然需要尝试找出如何实例化接口然后失败。

由于我的对象已被正确注释以使用Json.NET,我采用了不同的路径:我已经实现了一个自定义模型绑定器并使用Json.NET显式反序列化请求正文数据,如下所示:

 internal class CustomModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { // use Json.NET to deserialize the incoming Position controllerContext.HttpContext.Request.InputStream.Position = 0; // see: http://stackoverflow.com/a/3468653/331281 Stream stream = controllerContext.RequestContext.HttpContext.Request.InputStream; var readStream = new StreamReader(stream, Encoding.UTF8); string json = readStream.ReadToEnd(); return JsonConvert.DeserializeObject(json, ...); } } 

自定义模型绑定程序在Global.asax.cs注册:

  ModelBinders.Binders.Add(typeof(MyClass), new CustomModelBinder();