¿Cómo usar Json.NET para el enlace de modelos JSON en un proyecto MVC5?

He estado buscando en Internet una respuesta o ejemplo, pero todavía no he podido encontrar uno. Simplemente me gustaría cambiar el serializador JSON predeterminado que se utiliza para deserializar JSON mientras enlaza el modelo a la biblioteca JSON.NET.

Encontré esta publicación SO, pero no puedo implementarla hasta el momento, ni siquiera puedo ver el espacio de nombres System.Net.Http.Formatters , ni puedo ver GlobalConfiguration .

¿Qué me estoy perdiendo?

ACTUALIZAR

Tengo un proyecto ASP.NET MVC, básicamente era un proyecto MVC3. Actualmente estoy apuntando .NET 4.5 y usando ASP.NET MVC 5 y paquetes NuGet relacionados.

No veo el ensamblado System.Web.Http ni ningún espacio de nombres similar. En este contexto, me gustaría inyectar JSON.NET para ser utilizado como el enlace de modelo predeterminado para el tipo de solicitudes JSON.

Finalmente encontré una respuesta. Básicamente, no necesito las cosas de MediaTypeFormatter , que no están diseñadas para ser utilizadas en el entorno MVC, sino en ASP.NET Web API, por eso no veo esas referencias y espacios de nombres (por cierto, están incluidos en Microsoft.AspNet.WeApi NuGet).

La solución es usar una fábrica de proveedores de valores personalizados. Aquí está el código requerido.

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

Y puede usarlo así en su método Application_Start :

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

Aquí está la publicación que me indicó la dirección correcta, y también esta dio una buena explicación sobre los proveedores de valor y los encuadernadores.

También tuve un problema con esto. Estaba publicando JSON en una acción, pero mis nombres JsonProperty fueron ignorados. Por lo tanto, las propiedades de mi modelo siempre estaban vacías.

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

Estoy publicando en una acción usando esta función jquery personalizada:

 (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)); 

Y lo llamo así:

 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 }); 

Desafortunadamente, solo foo alguna vez se unió (extraño, ya que el caso no es el mismo, pero supongo que el modelo predeterminado no es sensible a mayúsculas y minúsculas)

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

La solución terminó siendo bastante simple

Acabo de implementar una versión genérica de la carpeta modelo de Dejan que funciona muy bien para mí. Probablemente podría usar algunos controles falsos (como asegurarse de que la solicitud sea en realidad application / json), pero está haciendo el truco en este momento.

 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); } } 

Cuando quiero usarlo en una acción específica, simplemente le digo que, en su lugar, quiero usar mi carpeta personalizada modelo Json.Net:

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

Ahora mis atributos [JsonProperty (PropertyName = “”)] ya no se ignoran en MyModel y todo está vinculado correctamente.

En mi caso, tuve que deserializar objetos complejos, incluidas las interfaces y los tipos cargados dinámicamente, etc., por lo que proporcionar un proveedor de valor personalizado no funciona, ya que MVC aún necesita tratar de descubrir cómo crear instancias de interfaces y luego falla.

Como mis objetos ya estaban anotados correctamente para trabajar con Json.NET, tomé una ruta diferente: implementé un encuadernador de modelo personalizado y utilicé Json.NET para deserializar explícitamente los datos del cuerpo de la solicitud como este:

 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, ...); } } 

El archivador de modelo personalizado está registrado en Global.asax.cs :

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