it-swarm.com.de

JSON-Daten in ASP.NET MVC buchen

Ich versuche, mit JSON eine Liste von Werbebuchungen auf eine Webseite zu bekommen, die dann bearbeitet und von ajax request an den Server zurückgesendet wird, wobei dieselbe JSON-Struktur verwendet wird (außer dass sich Feldwerte geändert haben). 

Der Empfang von Daten vom Server ist einfach und die Manipulation noch einfacher! Senden Sie diese JSON-Daten zurück an den Server, um Zeit zu sparen ... Selbstmordzeit! BITTE kann jemand helfen!

Javascript

var lineitems;

// get data from server
$.ajax({
    url: '/Controller/GetData/',
    success: function(data){
        lineitems = data;
    }
});

// post data to server
$.ajax({
    url: '/Controller/SaveData/',
    data: { incoming: lineitems }
});

C # - Objekte

public class LineItem{
    public string reference;
    public int quantity;
    public decimal amount;
}

C # - Controller

public JsonResult GetData()
{
    IEnumerable<LineItem> lineItems = ... ; // a whole bunch of line items
    return Json(lineItems);
}

public JsonResult SaveData(IEnumerable<LineItem> incoming){
    foreach(LineItem item in incoming){
        // save some stuff
    }
    return Json(new { success = true, message = "Some message" });
}

Die Daten kommen auf dem Server als serialisierte Postdaten an. Der automatisierte Modellbinder versucht, IEnumerable<LineItem> incoming zu binden, und erhält überraschenderweise die resultierende IEnumerable mit der korrekten Anzahl von LineItems - es werden nur Daten nicht mit Daten gefüllt.

L&OUML;SUNG

Mit Antworten aus verschiedenen Quellen, hauptsächlich djch in einem anderen stackoverflow post und BeRecursive unten, löste ich mein Problem mit zwei Hauptmethoden.

Serverseitig 

Der folgende Deserialisierer erfordert einen Verweis auf System.Runtime.Serialization und using System.Runtime.Serialization.Json

    private T Deserialise<T>(string json)
    {
        using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
        {
            var serialiser = new DataContractJsonSerializer(typeof(T));
            return (T)serialiser.ReadObject(ms);
        }
    }

    public void Action(int id, string items){
        IEnumerable<LineItem> lineitems = Deserialise<IEnumerable<LineItem>>(items);
        // do whatever needs to be done - create, update, delete etc.
    }

Client-Seite

Es verwendet die Stringify-Methode von json.org, die in dieser abhängigen Datei verfügbar ist. https://github.com/douglascrockford/JSON-js/blob/master/json2.js (2,5 KB bei Minifizierung)

        $.ajax({
            type: 'POST',
            url: '/Controller/Action',
            data: { 'items': JSON.stringify(lineItems), 'id': documentId }
        });
56
Jimbo

Werfen Sie einen Blick auf den Beitrag von Phil Haack über JSON-Daten für Modellbindung . Das Problem ist, dass der Standardmodellbinder JSON nicht ordnungsgemäß serialisiert. Sie benötigen eine Art ValueProvider OR, in dem Sie einen benutzerdefinierten Modellbinder schreiben können:

using System.IO;
using System.Web.Script.Serialization;

public class JsonModelBinder : DefaultModelBinder {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            if(!IsJSONRequest(controllerContext)) {
                return base.BindModel(controllerContext, bindingContext);
            }

            // Get the JSON data that's been posted
            var request = controllerContext.HttpContext.Request;
            //in some setups there is something that already reads the input stream if content type = 'application/json', so seek to the begining
            request.InputStream.Seek(0, SeekOrigin.Begin);
            var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();

            // Use the built-in serializer to do the work for us
            return new JavaScriptSerializer()
                .Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);

            // -- REQUIRES .NET4
            // If you want to use the .NET4 version of this, change the target framework and uncomment the line below
            // and comment out the above return statement
            //return new JavaScriptSerializer().Deserialize(jsonStringData, bindingContext.ModelMetadata.ModelType);
        }

        private static bool IsJSONRequest(ControllerContext controllerContext) {
            var contentType = controllerContext.HttpContext.Request.ContentType;
            return contentType.Contains("application/json");
        }
    }

public static class JavaScriptSerializerExt {
        public static object Deserialize(this JavaScriptSerializer serializer, string input, Type objType) {
            var deserializerMethod = serializer.GetType().GetMethod("Deserialize", BindingFlags.NonPublic | BindingFlags.Static);

            // internal static method to do the work for us
            //Deserialize(this, input, null, this.RecursionLimit);

            return deserializerMethod.Invoke(serializer,
                new object[] { serializer, input, objType, serializer.RecursionLimit });
        }
    }

Und weisen Sie MVC an, es in Ihrer Global.asax-Datei zu verwenden:

ModelBinders.Binders.DefaultBinder = new JsonModelBinder();

Außerdem verwendet dieser Code den Inhalt type = 'application/json'. Stellen Sie also sicher, dass Sie dies in jquery so einstellen:

$.ajax({
    dataType: "json",
    contentType: "application/json",            
    type: 'POST',
    url: '/Controller/Action',
    data: { 'items': JSON.stringify(lineItems), 'id': documentId }
});
29
BeRecursive

Der einfachste Weg, dies zu tun

Ich fordere Sie auf, diesen Blogbeitrag zu lesen, der Ihr Problem direkt anspricht.

Die Verwendung von benutzerdefinierten Modellbindern ist nicht wirklich sinnvoll, wie Phil Haack hervorgehoben hat (sein Blogeintrag ist auch im oberen Blogeintrag verlinkt).

Grundsätzlich haben Sie drei Optionen:

  1. Schreiben Sie JsonValueProviderFactory und verwenden Sie eine clientseitige Bibliothek wie json2.js, um direkt mit JSON zu kommunizieren.

  2. Schreiben Sie ein JQueryValueProviderFactory, das die jQuery-JSON-Objekttransformation in $.ajax oder versteht

  3. Verwenden Sie das sehr einfache und schnelle jQuery-Plugin, das im Blogeintrag beschrieben wird. Es bereitet jedes JSON-Objekt (sogar Arrays vor, das an IList<T> und Datums gebunden ist, die auf der Serverseite ordnungsgemäß analysiert werden als DateTime-Instanzen), die von Asp.net MVCs Standardmodellbinder verstanden werden.

Von allen dreien ist der letzte der einfachste und stört die inneren Funktionen von Asp.net MVC nicht, wodurch die mögliche Fehleroberfläche verringert wird. Wenn Sie diese im Blog-Beitrag beschriebene Technik verwenden, werden Daten mit den starken Typaktionsparametern korrekt binden und ebenfalls validiert. Es ist also im Grunde eine Win-Win-Situation.

12
Robert Koritnik

In MVC3 wurde dies hinzugefügt.

Was aber noch schöner ist, dass MVC-Quellcode geöffnet ist, können Sie den ValueProvider verwenden und ihn selbst in Ihrem eigenen Code verwenden (falls Sie noch nicht auf MVC3 sind).

Sie werden mit so etwas enden

ValueProviderFactories.Factories.Add(new JsonValueProviderFactory())
8
Kenny Eliasson

Ich habe dieses Problem mit den folgenden Tipps von vestigal gelöst:

Kann ich in web.config eine unbegrenzte Länge für maxJsonLength festlegen?

Wenn ich einen großen Json für eine Aktion in einem Controller bereitstellen musste, würde ich den berühmten "Fehler bei der Deserialisierung mit dem JSON-JavaScriptSerializer" erhalten. Die Länge der Zeichenfolge überschreitet den in der Eigenschaft maxJsonLength festgelegten Wert Wertanbieter ".

Was ich getan habe, ist eine neue ValueProviderFactory, LargeJsonValueProviderFactory, zu erstellen und MaxJsonLength = Int32.MaxValue in der GetDeserializedObject-Methode festzulegen

public sealed class LargeJsonValueProviderFactory : ValueProviderFactory
{
    private static void AddToBackingStore(LargeJsonValueProviderFactory.EntryLimitedDictionary backingStore, string prefix, object value)
    {
        IDictionary<string, object> dictionary = value as IDictionary<string, object>;
        if (dictionary != null)
        {
            foreach (KeyValuePair<string, object> keyValuePair in (IEnumerable<KeyValuePair<string, object>>) dictionary)
                LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakePropertyKey(prefix, keyValuePair.Key), keyValuePair.Value);
        }
        else
        {
            IList list = value as IList;
            if (list != null)
            {
                for (int index = 0; index < list.Count; ++index)
                    LargeJsonValueProviderFactory.AddToBackingStore(backingStore, LargeJsonValueProviderFactory.MakeArrayKey(prefix, index), list[index]);
            }
            else
                backingStore.Add(prefix, value);
        }
    }

    private static object GetDeserializedObject(ControllerContext controllerContext)
    {
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            return (object) null;
        string end = new StreamReader(controllerContext.HttpContext.Request.InputStream).ReadToEnd();
        if (string.IsNullOrEmpty(end))
            return (object) null;

        var serializer = new JavaScriptSerializer {MaxJsonLength = Int32.MaxValue};

        return serializer.DeserializeObject(end);
    }

    /// <summary>Returns a JSON value-provider object for the specified controller context.</summary>
    /// <returns>A JSON value-provider object for the specified controller context.</returns>
    /// <param name="controllerContext">The controller context.</param>
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");
        object deserializedObject = LargeJsonValueProviderFactory.GetDeserializedObject(controllerContext);
        if (deserializedObject == null)
            return (IValueProvider) null;
        Dictionary<string, object> dictionary = new Dictionary<string, object>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
        LargeJsonValueProviderFactory.AddToBackingStore(new LargeJsonValueProviderFactory.EntryLimitedDictionary((IDictionary<string, object>) dictionary), string.Empty, deserializedObject);
        return (IValueProvider) new DictionaryValueProvider<object>((IDictionary<string, object>) dictionary, CultureInfo.CurrentCulture);
    }

    private static string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString((IFormatProvider) CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        if (!string.IsNullOrEmpty(prefix))
            return prefix + "." + propertyName;
        return propertyName;
    }

    private class EntryLimitedDictionary
    {
        private static int _maximumDepth = LargeJsonValueProviderFactory.EntryLimitedDictionary.GetMaximumDepth();
        private readonly IDictionary<string, object> _innerDictionary;
        private int _itemCount;

        public EntryLimitedDictionary(IDictionary<string, object> innerDictionary)
        {
            this._innerDictionary = innerDictionary;
        }

        public void Add(string key, object value)
        {
            if (++this._itemCount > LargeJsonValueProviderFactory.EntryLimitedDictionary._maximumDepth)
                throw new InvalidOperationException("JsonValueProviderFactory_RequestTooLarge");
            this._innerDictionary.Add(key, value);
        }

        private static int GetMaximumDepth()
        {
            NameValueCollection appSettings = ConfigurationManager.AppSettings;
            if (appSettings != null)
            {
                string[] values = appSettings.GetValues("aspnet:MaxJsonDeserializerMembers");
                int result;
                if (values != null && values.Length > 0 && int.TryParse(values[0], out result))
                    return result;
            }
            return 1000;
        }
    }
}

Ersetzen Sie dann in der Application_Start-Methode von Global.asax.cs die ValueProviderFactory durch die neue:

protected void Application_Start()
    {
        ...

        //Add LargeJsonValueProviderFactory
        ValueProviderFactory jsonFactory = null;
        foreach (var factory in ValueProviderFactories.Factories)
        {
            if (factory.GetType().FullName == "System.Web.Mvc.JsonValueProviderFactory")
            {
                jsonFactory = factory;
                break;
            }
        }

        if (jsonFactory != null)
        {
            ValueProviderFactories.Factories.Remove(jsonFactory);
        }

        var largeJsonValueProviderFactory = new LargeJsonValueProviderFactory();
        ValueProviderFactories.Factories.Add(largeJsonValueProviderFactory);
    }
3
MFA

Sie können diese probieren .. 1. stringify Ihr JSON-Objekt vor dem Aufruf der Serveraktion über ajax 2. Deserialisieren Sie den String in der Aktion und verwenden Sie die Daten als Wörterbuch.

Javascript-Beispiel unten (Senden des JSON-Objekts)

$.ajax(
   {
       type: 'POST',
       url: 'TheAction',
       data: { 'data': JSON.stringify(theJSONObject) 
   }
})

Aktionsbeispiel (C #) unten

[HttpPost]
public JsonResult TheAction(string data) {

       string _jsonObject = data.Replace(@"\", string.Empty);
       var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();           
        Dictionary<string, string> jsonObject = serializer.Deserialize<Dictionary<string, string>>(_jsonObject);


        return Json(new object{status = true});

    }

BeRecursives Antwort ist die, die ich verwendet habe, so dass wir auf Json.Net standardisieren könnten (wir haben MVC5 und WebApi 5 - WebApi 5 verwendet bereits Json.Net), aber ich habe ein Problem gefunden. Wenn sich auf Ihrer Route Parameter befinden, für die Sie POSTing ausführen, versucht MVC, den Modellbinder für die URI-Werte aufzurufen, und dieser Code versucht, den bereitgestellten JSON an diese Werte zu binden.

Beispiel:

[HttpPost]
[Route("Customer/{customerId:int}/Vehicle/{vehicleId:int}/Policy/Create"]
public async Task<JsonNetResult> Create(int customerId, int vehicleId, PolicyRequest policyRequest)

Die BindModel-Funktion wird dreimal aufgerufen, wobei beim ersten Versuch ein Bombenangriff ausgeführt wird, da versucht wird, die JSON mit dem Fehler an customerId zu binden: Error reading integer. Unexpected token: StartObject. Path '', line 1, position 1.

Ich habe diesen Codeblock oben in BindModel hinzugefügt:

if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName) != null) {
    return base.BindModel(controllerContext, bindingContext);
}

Glücklicherweise verfügt der ValueProvider über Routing-Werte, sobald er zu dieser Methode kommt.

0
YakkoWarner

Wenn Sie die JSON-Daten als Zeichenfolge erhalten (z. B. '[{"id": 1, "name": "Charles"}, {"id": 8, "name": "John"}, { "id": 13, "name": "Sally"}] ')

Dann würde ich JSON.net verwenden und Linq to JSON verwenden, um die Werte herauszuholen ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

    if (Request["items"] != null)
    {
        var items = Request["items"].ToString(); // Get the JSON string
        JArray o = JArray.Parse(items); // It is an array so parse into a JArray
        var a = o.SelectToken("[0].name").ToString(); // Get the name value of the 1st object in the array
        // a == "Charles"
    }
}
}
0
David Henderson

Ich habe es mit einer "manuellen" Deserialisierung gelöst . Ich erkläre es im Code

public ActionResult MyMethod([System.Web.Http.FromBody] MyModel model)
{
 if (module.Fields == null && !string.IsNullOrEmpty(Request.Form["fields"]))
 {
   model.Fields = JsonConvert.DeserializeObject<MyFieldModel[]>(Request.Form["fields"]);
 }
 //... more code
}
0
Antonio Santise