Публикация данных JSON в ASP.NET MVC
Im пытается получить список элементов строки на веб-страницу с помощью JSON, который затем будет обработан и отправлен обратно на сервер по запросу ajax, используя ту же структуру JSON, которая прибыла (за исключением изменения значений полей).
получение данных с сервера легко, манипуляции еще проще! но отправка этих данных JSON обратно на сервер для сохранения... время самоубийства! Пожалуйста, может кто-то помогите!
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# - Объекты
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" });
}
данные поступают на сервер, как сериализовать данные. Автоматическая модель связующего пытается связать IEnumerable<LineItem> incoming и неожиданно получает в результате IEnumerable имеет правильное число LineItems - он просто не заполнять данные.
решение
используя ответы из ряда источников, в первую очередь djch на другом столбе stackoverflow и BeRecursive ниже я решил свою проблему, используя два основных метода.
На Стороне Сервера
десериализатор ниже требует Ссылки на System.Runtime.Serialization и 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.
}
На Стороне Клиента
он использует json.org ' s stringify метод, доступный в этом dependecy https://github.com/douglascrockford/JSON-js/blob/master/json2.js (что составляет 2,5 КБ при уменьшении)
$.ajax({
type: 'POST',
url: '/Controller/Action',
data: { 'items': JSON.stringify(lineItems), 'id': documentId }
});
8 ответов:
взгляните на пост Фила Хаака на модель привязки данных в формате JSON. Проблема в том, что по умолчанию модель связующего не правильно сериализовать в JSON. Вам нужен какой-то ValueProvider или вы можете написать пользовательскую модель binder:
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 }); } }и скажите MVC, чтобы использовать его в своем глобальном.файл asax:
ModelBinders.Binders.DefaultBinder = new JsonModelBinder();кроме того, этот код использует тип контента = 'application / json' поэтому убедитесь, что вы установили это в jquery следующим образом:
$.ajax({ dataType: "json", contentType: "application/json", type: 'POST', url: '/Controller/Action', data: { 'items': JSON.stringify(lineItems), 'id': documentId } });
самый простой способ сделать это
Я призываю вас читать этот блог, который непосредственно решает вашу проблему.
использование пользовательских связующих моделей на самом деле не так мудро, как указал Фил Хаак (его сообщение в блоге также связано с верхним сообщением в блоге).
в основном у вас есть три варианты:
написать
JsonValueProviderFactoryи использовать библиотеку на стороне клиента, какjson2.jsдля общения остроумие JSON напрямую.написать
JQueryValueProviderFactoryэто понимает преобразование объекта jQuery JSON, которое происходит в$.ajaxилииспользуйте очень простой и быстрый плагин jQuery, описанный в блоге, который готовит любой объект JSON (даже массивы это будет связано с
IList<T>и времени что будет правильно разобрать на стороне сервера какDateTimeэкземпляры), которые будут поняты Asp.net модель по умолчанию MVC связующее вещество.из всех трех, последний является самым простым и не мешает Asp.net внутренние работы MVC таким образом понижая возможную поверхность ошибки. Использование этого метода, описанного в сообщении в блоге, позволит правильно привязать данные к параметрам действия сильного типа и проверить их. Так что это в основном победа.
в MVC3 они добавили это.
но что еще более приятно, так это то, что поскольку исходный код MVC открыт, вы можете захватить ValueProvider и использовать его самостоятельно в своем собственном коде (если вы еще не на MVC3).
вы получите что-то вроде этого
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory())
Я решил эту проблему следующим Вот советы выстригает это:
могу ли я установить неограниченную длину для maxJsonLength в интернете.конфиг?
когда мне нужно было опубликовать большой json для действия в контроллере, я бы получил знаменитую "ошибку во время десериализации с помощью JSON JavaScriptSerializer. Длина строки превышает значение, установленное для свойства maxJsonLength.\r\nParameter имя: поставщик входных значений".
то, что я сделал, это создать новый ValueProviderFactory, LargeJsonValueProviderFactory и установите MaxJsonLength = Int32.MaxValue в методе GetDeserializedObject
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; } } }затем, в методе Application_Start в Global.асакс.cs, замените ValueProviderFactory на новый:
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); }
Вы можете попробовать эти. 1. stringify свой объект JSON перед вызовом действия сервера через ajax 2. десериализуйте строку в действии, а затем используйте данные в качестве словаря.
пример Javascript ниже (отправка объекта JSON
$.ajax( { type: 'POST', url: 'TheAction', data: { 'data': JSON.stringify(theJSONObject) } })пример действия (C#) ниже
[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}); }
Если у вас есть данные JSON, поступающие в виде строки (например, ' [{"id": 1, "name": "Charles"}, {"id": 8, "name": "John"}, {"id": 13,"name":"Sally"}]')
затем я хотел бы использовать JSON.net и использовать LINQ для JSON для получения значений...
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" } } }
ответ Берекур это тот, который я использовал, так что мы могли бы стандартизировать на Json.Net (у нас есть MVC5 и WebApi 5 -- WebApi 5 уже использует Json.Net), но я нашел проблему. Когда у вас есть параметры в вашем маршруте, к которому вы проводите, MVC пытается вызвать привязку модели для значений URI, и этот код попытается привязать разнесенный JSON к этим значениям.
пример:
[HttpPost] [Route("Customer/{customerId:int}/Vehicle/{vehicleId:int}/Policy/Create"] public async Task<JsonNetResult> Create(int customerId, int vehicleId, PolicyRequest policyRequest)The
BindModelфункция вызывается три раза, взрыв на во-первых, как он пытается привязать JSON вcustomerIdошибка:Error reading integer. Unexpected token: StartObject. Path '', line 1, position 1.я добавил этот блок кода в начало
BindModel:if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName) != null) { return base.BindModel(controllerContext, bindingContext); }ValueProvider, к счастью, имеет значения маршрута, вычисленные к тому времени, когда он добирается до этого метода.
Я решил с помощью "ручного" десериализации. Я объясню в коде
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 }
Comments