Как преобразовать модель представления в объект JSON в ASP.NET MVC?

156

Я разработчик Java, новичок в .NET. Я работаю над проектом .NET MVC2, где я хочу иметь частичное представление, чтобы обернуть виджет. Каждый объект виджета JavaScript имеет объект данных JSON, который будет заполнен данными модели. Затем методы для обновления этих данных привязываются к событиям, когда данные изменяются в виджете или если эти данные изменяются в другом виджете.

Код выглядит примерно так:

MyController:

virtual public ActionResult DisplaySomeWidget(int id) {
  SomeModelView returnData = someDataMapper.getbyid(1);

  return View(myview, returnData);
}

myview.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SomeModelView>" %>

<script type="text/javascript">
  //creates base widget object;
  var thisWidgetName = new Widget();

  thisWidgetName.updateTable = function() {
    //  UpdatesData
  };
  $(document).ready(function () {
    thisWidgetName.data = <% converttoJSON(model) %>
    $(document).bind('DATA_CHANGED', thisWidgetName.updateTable());
  });
</script>

<div><%:model.name%></div>

Чего я не знаю, так это как отправить данные, а SomeModelViewзатем использовать их для заполнения виджета, а также преобразовать их в JSON. Я видел несколько действительно простых способов сделать это в контроллере, но не в представлении. Я полагаю, что это основной вопрос, но я уже несколько часов пытаюсь сделать это замечательно.

Крис Стивенс
источник
1
Я знаю, что это старый вопрос. Но на сегодняшний день есть лучшие способы сделать это. Не смешивайте JSON inline с результатами просмотра. JSON легко сериализуется через AJAX и может рассматриваться как объекты. Все в JavaScript должно быть отделено от View. Вы можете легко вернуть модели без каких-либо усилий через контроллер.
Петр Кула

Ответы:

346

В mvc3 с бритвой, @Html.Raw(Json.Encode(object))кажется, добиваются цели .

Дейв
источник
1
+1 Я использовал Html.Raw, но так и не нашел Json.Encode и просто использовал JavaScriptSerializer для добавления строки в контроллере в модель представления
AaronLS
5
Этот подход работает, даже если вы хотите передать полученный JSON в Javascript. Razor жалуется на зеленые загогулины, если вы помещаете код @ Html.Raw (...) в качестве параметра функции в теги <script>, но JSON действительно делает это для вызываемого JS. Очень удобно и гладко. +1
Карл Генрих Ханке
1
Это способ сделать это начиная с MVC3 и должен быть возвращен из контроллера. Не вставляйте JSON в свой Viewresult. Вместо этого создайте контроллер для обработки JSON отдельно и выполните запрос JSON через AJAX. Если вам нужен JSON, вы делаете что-то не так. Или используйте ViewModel или что-то еще.
Петр Кула
3
Json.Encode кодирует мой 2-мерный массив в 1-мерный массив в JSON. Newtonsoft.Json.JsonConvert.SerializeObject правильно сериализует эти два измерения в json. Поэтому я предлагаю использовать последний.
mono68
12
При использовании MVC 6 (возможно, также 5) вам нужно использовать Json.Serializeвместо Encode.
KoalaBear
31

Отлично, вы только начали использовать MVC и обнаружили первый серьезный недостаток.

Вы действительно не хотите преобразовывать его в JSON в представлении, и вы действительно не хотите преобразовывать его в контроллере, поскольку ни одно из этих мест не имеет смысла. К сожалению, вы застряли в этой ситуации.

Лучшее, что я нашел, это отправил JSON в представление в ViewModel, например так:

var data = somedata;
var viewModel = new ViewModel();
var serializer = new JavaScriptSerializer();
viewModel.JsonData = serializer.Serialize(data);

return View("viewname", viewModel);

затем используйте

<%= Model.JsonData %>

по вашему мнению. Имейте в виду, что стандартный .NET JavaScriptSerializer довольно дерьмо.

выполнение этого в контроллере, по крайней мере, делает его тестируемым (хотя не совсем так, как описано выше - вы, вероятно, захотите взять ISerializer в качестве зависимости, чтобы вы могли его посмеяться)

Обновите также, что касается вашего JavaScript, было бы неплохо обернуть ВСЕ виджеты JS, которые у вас есть выше, примерно так:

(
    // all js here
)();

таким образом, если вы поместите несколько виджетов на страницу, вы не получите конфликтов (если только вам не нужен доступ к методам из других мест на странице, но в этом случае вы все равно должны зарегистрировать виджет с некоторой структурой виджетов). Сейчас это может не быть проблемой, но было бы неплохо добавить скобки сейчас, чтобы сэкономить время, когда это станет требованием, а также хорошей практикой ОО для инкапсуляции функциональности.

Эндрю Баллок
источник
1
Это выглядит интересно. Я собирался просто сделать копию данных в формате json и передать их как viewData, но таким образом это выглядит более интересным. Я поиграю с этим и дам вам знать. Кстати, я впервые публикую вопрос о stackoverflow, и мне потребовалось 5 минут, чтобы получить хорошие ответы, это здорово !!
Крис Стивенс
Ничего плохого в этом нет, его просто нельзя настроить, если вам нужно какое-либо пользовательское форматирование значений, вы должны сделать это заранее, в основном делая все в виде строки :( это наиболее заметно с датами. Я знаю, что есть простые обходные пути, но они не должны быть необходимо!
Андрей Баллок
@ Обновление Я собирался увидеть использование статического счетчика .net этого виджета для генерации уникального имени объекта. Но то, что вы написали, выглядит так, как будто это сделало бы то же самое и сделало бы это гладким Кроме того, я изучил привязку создания виджета «new_widget_event» к объекту уровня страницы, чтобы отслеживать их, но первоначальной причиной для этого стал OBE. Поэтому я мог бы вернуться к этому позже. Спасибо за всю помощь, которую я собираюсь опубликовать модифицированная версия того, что вы предложили, но объясните, почему мне это нравится больше
Крис Стивенс,
2
я отказываюсь от своего предыдущего заявления "в этом нет ничего плохого". Там все с этим не так.
Эндрю Баллок
Почему вы говорите, что мы не можем вернуть JSON из контроллера. Вот как это должно быть сделано. Используя JavaScriptSerializerили Return Json(object)оба используют те же сериализаторы. Кроме того, отправка того же JSON обратно на контроллер перестроит объект для вас, если вы определите правильную модель. Может быть, во время MVC2 это было серьезным недостатком ... но сегодня это бриз и очень удобно. Вы должны обновить свой ответ, чтобы отразить это.
Петр Кула
18

Я нашел, что было бы неплохо сделать это так (использование в представлении):

    @Html.HiddenJsonFor(m => m.TrackingTypes)

Вот соответствующий класс Extension вспомогательного метода:

public static class DataHelpers
{
    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        return HiddenJsonFor(htmlHelper, expression, (IDictionary<string, object>) null);
    }

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        return HiddenJsonFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        var name = ExpressionHelper.GetExpressionText(expression);
        var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        var tagBuilder = new TagBuilder("input");
        tagBuilder.MergeAttributes(htmlAttributes);
        tagBuilder.MergeAttribute("name", name);
        tagBuilder.MergeAttribute("type", "hidden");

        var json = JsonConvert.SerializeObject(metadata.Model);

        tagBuilder.MergeAttribute("value", json);

        return MvcHtmlString.Create(tagBuilder.ToString());
    }
}

Он не суперсложен, но он решает проблему того, где его разместить (в контроллере или в поле зрения?). Ответ очевиден: ни;)

Wolfgang
источник
Это было красиво и чисто, на мой взгляд, и завернуто в многоразового помощника. Приветствия, J
Джон
6

Вы можете использовать Jsonиз действия напрямую,

Ваше действие будет примерно таким:

virtual public JsonResult DisplaySomeWidget(int id)
{
    SomeModelView returnData = someDataMapper.getbyid(id);
    return Json(returnData);
}

редактировать

Просто увидел, что вы предполагаете, что это представление, Modelпоэтому вышеприведенное не совсем правильно, вам нужно сделать Ajaxвызов метода контроллера, чтобы получить это, ascxтогда у вас не будет модели как таковой, я оставлю свой код на всякий случай, если это будет полезно для вас, и вы можете изменить вызов

Edit 2 просто вставьте идентификатор в код

Pharabus
источник
1
но он не может представить это в поле зрения, ему придется сделать второй вызов ajax
Эндрю Баллок
Спасибо, я изначально делал это, используя jQuery get json call, и планировал, чтобы элементы HTML заполняли себя из json. Однако шаблон, которому мы следуем сейчас, состоит в том, что наши представления должны возвращать modelView, и это самый простой способ заполнить базовые элементы HTML, такие как таблицы и т. Д. Я мог бы отправить те же данные в формате JSON, что и ViewData, но это кажется расточительным.
Крис Стивенс
2
ВЫ НЕ ДОЛЖНЫ встраивать JSON с результатом просмотра. ОП должен либо придерживаться стандартной практики MVC и возвращать модель или модель представления, либо выполнять AJAX. Нет абсолютно НИКАКОЙ ПРИЧИНЫ встраивать JSON в представление. Это просто очень грязный код. Этот ответ является правильным и рекомендуемым способом практики Microsoft. Понижение было ненужным, это, безусловно, правильный ответ. Мы не должны поощрять плохую практику кодирования. Либо JSON через AJAX, либо Модели через представления. Никто не любит спагетти-код со смешанной разметкой!
Петр Кула
Это решение приведет к следующей проблеме: stackoverflow.com/questions/10543953/…
Дженни О'Рейли
2

@ Html.Raw (Json.Encode (object)) можно использовать для преобразования модального объекта View в JSON

Прия Пайявула
источник
1

Продолжая отличный ответ от Дейва . Вы можете создать простой HtmlHelper .

public static IHtmlString RenderAsJson(this HtmlHelper helper, object model)
{
    return helper.Raw(Json.Encode(model));
}

И по вашему мнению:

@Html.RenderAsJson(Model)

Таким образом, вы можете централизовать логику для создания JSON, если по какой-то причине вы захотите изменить логику позже.

smoksnes
источник
0

У Эндрю был отличный отклик, но я хотел немного его настроить. Отличие заключается в том, что мне нравится, что мои ModelViews не содержат служебных данных. Просто данные для объекта. Кажется, что ViewData подходит для накладных расходов, но, конечно, я новичок в этом. Я предлагаю сделать что-то подобное.

контроллер

virtual public ActionResult DisplaySomeWidget(int id)
{
    SomeModelView returnData = someDataMapper.getbyid(1);
    var serializer = new JavaScriptSerializer();
    ViewData["JSON"] = serializer.Serialize(returnData);
    return View(myview, returnData);
}

Посмотреть

//create base js object;
var myWidget= new Widget(); //Widget is a class with a public member variable called data.
myWidget.data= <%= ViewData["JSON"] %>;

Это делает для вас то, что вы получаете те же данные в вашем JSON, что и в ModelView, так что вы можете вернуть JSON обратно в контроллер, и он будет иметь все части. Это похоже на простой запрос через JSONRequest, однако для этого требуется на один вызов меньше, что экономит ваши накладные расходы. Кстати, это забавно для Даты, но это похоже на другой поток.

Крис Стивенс
источник