Come utilizzare JSON.NET per la modellazione di modelli JSON in un progetto MVC5?

Ho cercato su Internet una risposta o un esempio, ma non sono riuscito a trovarne uno. Mi piacerebbe semplicemente cambiare il serializzatore JSON predefinito che viene utilizzato per deserializzare JSON durante la modellazione del modello alla libreria JSON.NET.

Ho trovato questo post SO, ma non riesco ad implementarlo finora, non riesco nemmeno a vedere lo spazio System.Net.Http.Formatters nomi System.Net.Http.Formatters , né posso vedere GlobalConfiguration .

Cosa mi manca?

AGGIORNARE

Ho un progetto ASP.NET MVC, era fondamentalmente un progetto MVC3. Attualmente sto studiando .NET 4.5 e l’utilizzo di ASP.NET MVC 5 e dei pacchetti NuGet correlati.

Non vedo l’assembly System.Web.Http né uno spazio dei nomi simile. In questo contesto mi piacerebbe iniettare JSON.NET da utilizzare come modello predefinito per il tipo di richieste JSON.

Ho finalmente trovato una risposta. Fondamentalmente non ho bisogno del materiale MediaTypeFormatter , che non è progettato per essere utilizzato nell’ambiente MVC, ma nelle API Web ASP.NET, è per questo che non vedo quei riferimenti e spazi dei nomi (a proposito, quelli sono inclusi in Microsoft.AspNet.WeApi AspNet.WeApi Microsoft.AspNet.WeApi ).

La soluzione è utilizzare un fornitore di valore personalizzato. Ecco il codice richiesto.

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

E puoi usarlo in questo modo nel tuo metodo Application_Start :

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

Ecco il post che mi ha indirizzato nella giusta direzione, e anche questo ha fornito una buona spiegazione sui fornitori di valore e sui modelbinder.

Ho avuto anche questo problema con questo. Stavo postando JSON a un’azione, eppure i miei nomi di JsonProperty sono stati ignorati. Quindi, le mie proprietà del modello erano sempre vuote.

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

Sto postando su un’azione utilizzando questa funzione jquery personalizzata:

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

E lo chiamo così:

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

Sfortunatamente, solo il pippo è stato sempre vincolato (strano, dato che il caso non è lo stesso, ma suppongo che il modelbinder predefinito non sia sensibile al maiuscolo / minuscolo)

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

La soluzione si è rivelata piuttosto semplice

Ho appena implementato una versione generica del raccoglitore di modelli di Dejan che funziona molto bene per me. Potrebbe probabilmente usare alcuni controlli fittizi (come assicurarsi che la richiesta sia effettivamente application / json), ma sta facendo il trucco in questo 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); } } 

Quando voglio usarlo su un’azione specifica, dico semplicemente che voglio usare il mio raccoglitore modello Json.Net personalizzato:

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

Ora i miei attributi [JsonProperty (PropertyName = “”)] non vengono più ignorati su MyModel e tutto è vincolato correttamente!

Nel mio caso, ho dovuto deserializzare oggetti complessi, tra cui interfacce e tipi caricati dynamicmente, ecc., Quindi fornire un fornitore di valore personalizzato non funziona, poiché MVC ha ancora bisogno di cercare di capire come istanziare le interfacce e poi fallisce.

Poiché i miei oggetti erano già correttamente annotati per funzionare con Json.NET, ho preso una strada diversa: ho implementato un modello personalizzato e utilizzato Json.NET per deserializzare esplicitamente i dati del corpo della richiesta in questo modo:

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

Il modello personalizzato è registrato in Global.asax.cs :

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