Отправить массив объектов через JSON для ASP.Net MVC3
Я ищу решение для POSTing массива объектов MVC3 через JSON.
Пример кода, над которым я работаю:
http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
JS:
var data = { ItemList: [ {Str: 'hi', Enabled: true} ], X: 1, Y: 2 };
$.ajax({
url: '/list/save',
data: JSON.stringify(data),
success: success,
error: error,
type: 'POST',
contentType: 'application/json, charset=utf-8',
dataType: 'json'
});
ListViewModel.cs:
public class ListViewModel
{
public List<ItemViewModel> ItemList { get; set; }
public float X { get; set; }
public float Y { get; set; }
}
ItemViewModel.cs:
public class ItemViewModel
{
public string Str; // originally posted with: { get; set; }
public bool Enabled; // originally posted with: { get; set; }
}
ListController.cs:
public ActionResult Save(ListViewModel list)
{
// Do something
}
Результат этого POST:
список установлен в ListViewModel
Его свойства X и Y заданы
Введенное свойство ItemList установлено
Элемент ItemList содержит один элемент, так как он должен
Элемент в этом элементе ItemList не инициализирован. Str имеет значение null, а Enabled - false.
Другими словами, это то, что я получаю от привязки модели MVC3:
list.X == 1
list.Y == 2
list.ItemList != null
list.ItemList.Count == 1
list.ItemList[0] != null
list.ItemList[0].Str == null
Казалось бы, MVC3 JsonValueProvider не работает для сложных объектов. Как мне заставить это работать? Нужно ли мне модифицировать существующий MVC3 JsonValueProvider и исправить его? Если да, то как мне добраться до него и заменить его в проекте MVC3?
Связанные вопросы StackOverflow, которые я уже преследовал безрезультатно:
Asp.net Mvc Ajax Json (post Array)
Использует MVC2 и старую кодировку на основе форм - этот подход завершается неудачей с объектом, который содержит массив объектов (JQuery не может его правильно закодировать).
Опубликовать массив сложных объектов с помощью JSON, JQuery для ASP.NET MVC Controller
Использует хак, который я хотел бы избежать, когда контроллер получает обычную строку, которую он вручную десериализует самостоятельно, а не использует фреймворк.
MVC3 RC2 JSON Post Binding не работает правильно
У него не было своего набора контента - он установлен в моем коде.
Как разместить массив сложных объектов с помощью JSON, jQuery для ASP.NET MVC Controller?
Этот бедный парень должен был написать JsonFilter, чтобы разобрать массив. Еще один хак, который я бы предпочел избежать.
Итак, как мне это сделать?
Ответы
Ответ 1
Проблема заключалась в том, что свойства в моделях, которые находились в Списке, не имели/не устанавливали свои общедоступные свойства. Другими словами, автоматическое связывание JSON MVC3 работает только с объектами, которые имеют get и set.
Это не будет связывать:
public string Str;
Это свяжет:
public string Str { get; set; }
Ответ 2
В дополнение к { get; set; }
, это некоторые из условий поддержки привязки JSON:
- Это новая функция в ASP.NET MVC 3 (см. "Улучшения JavaScript и AJAX").
- Строки объектов JSON ('X,' Y, 'Str и' Enabled) должны соответствовать свойствам объектов ViewModel.
- Свойства объектов ViewModel должны иметь метод
{ get; set; }
.
- В запросе укажите тип содержимого как "application/json".
- Если он все еще не работает, проверьте строку JSON, чтобы убедиться, что она действительна.
Подробнее на мой пост.
Надеюсь, что это поможет!
Ответ 3
Это странно. Я не могу воспроизвести ваше поведение. Здесь моя настройка (ASP.NET MVC 3 RTM):
Модель:
public class ItemViewModel
{
public string Str { get; set; }
public bool Enabled { get; set; }
}
public class ListViewModel
{
public List<ItemViewModel> ItemList { get; set; }
public float X { get; set; }
public float Y { get; set; }
}
Контроллер:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Save(ListViewModel list)
{
return Json(list);
}
}
Вид:
@{
ViewBag.Title = "Home Page";
}
<script type="text/javascript">
$(function () {
var data = { ItemList: [{ Str: 'hi', Enabled: true}], X: 1, Y: 2 };
$.ajax({
url: '@Url.Action("save", "home")',
data: JSON.stringify(data),
type: 'POST',
contentType: 'application/json',
dataType: 'json',
success: function (result) {
alert(result.ItemList[0].Str);
}
});
});
</script>
Запуск этих предупреждений "hi"
и внутри действия Save
все правильно инициализировано.
И только для записи, что не работает, словари. Я открыл билет о проблеме.
Ответ 4
У меня была аналогичная проблема, и я обнаружил, что для сложного объекта числовые значения пропускаются. Они шли как нули.
то есть.
var person = {
Name: "john",
Age: 9
}
принимался контроллером MVC как объект Person, где свойства заполнялись как Name=John
и Age=0
.
Затем я сделал значение Age в Javascript для строки... i.e.
var person = {
Name: "john",
Age: "9"
}
И это получилось просто отлично...
Ответ 5
Это потому, что связки MVC вроде сосут. Однако они работают очень хорошо, если все значения JSON передаются как строка.
В JS, если вы это сделаете
var myObject = {thisNumber:1.6};
myObject.thisNumber=myObject.thisNumber-.6;
Он будет оценивать до 1 не до 1,0
Поэтому, когда вы отправили его на сервер, он попытается связать с поплавком этого имени, и он не найдет его, так как он появился как 1 вместо 1.0. Его очень хромой и безумный, что инженеры MS не придумали решение по умолчанию для этого. Я нахожу, что вы привязываете все привязки, достаточно умные, чтобы находить вещи.
Итак, перед отправкой данных запустите его, хотя строковый, который также преобразует все значения в строки.
Ответ 6
Все предыдущие ответы были замечательными, чтобы указать на решение подобной проблемы. Я должен был выполнить POST x-www-form-urlencoding
вместо application/json
(опция по умолчанию, если отсутствует параметр contentType), чтобы иметь возможность передавать __RequestVerificationToken
и одновременно сталкиваться с проблемой, когда свойства объекта, находящиеся в массиве, не связывают их значения. Способ решения проблемы заключается в понимании внутренней работы связующего объекта MVC.
Итак, в основном, когда вам нужно поставить токен проверки, вы ограничены атрибутом проверки. И вы должны предоставить токен в качестве параметра не как часть отправляемого объекта JSON. Если вы не использовали бы ValidateAntiForgeryToken
, вы могли бы согласиться с JSON.stringify. Но если вы это сделаете, вы не сможете передать токен.
Я обнюхал трафик на backend, когда ContentType
был x-www-form-urlencoding
, и я заметил, что мой массив сложных объектов был сериализован примерно так: klo[0][Count]=233&klo[0][Blobs]=94
. Этот массив первоначально был частью корневого объекта, скажем, некоторой модели. Это выглядело так: model.klo = [{ Count: 233, Blobs: 94}, ...]
.
На стороне бэкэнда это свойство klo
создавалось связующим MVC с теми же подсчетами элементов, которые я отправил. Но сами эти элементы не получили значений для своих свойств.
Решение
Чтобы справиться с этим, я исключил свойство klo
из объекта модели на стороне клиента. В функции ajax
я написал этот код:
data: $.param(model) + "&" + arrayOfObjectsToFormEncoding("klo", [{ Count: 233, Blobs: 94}, ...])
....
function arrayOfObjectsToFormEncoding (modelPropertyName, arrayOfObjects) {
var result = "";
if (arrayOfObjects && typeof arrayOfObjects == "object") {
for (var i = 0; i < arrayOfObjects.length; i++) {
var obj = arrayOfObjects[i];
if (obj) {
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
result += encodeURIComponent(modelPropertyName + "[" + i + "]." + p) + "=" + encodeURIComponent(obj[p]) + "&";
}
}
}
}
}
if (result[result.length - 1] == "&") {
result = result.substr(0, result.length - 1);
}
return result;
}
Функция преобразует массив сложного объекта в форму, которая распознается MVC-связующим. Форма klo[0].Count=233&klo[0].Blobs=94
.