Отправить массив объектов через 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.