Як перетворити модель перегляду в об'єкт 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 в рядці з результатом перегляду. JSON легко серіалізується через AJAX і може оброблятись як об'єкти. Все, що в JavaScript, має бути окремим від представлення даних. Ви можете легко повернути моделі без будь-яких зусиль через контролер.
Пьотр Кула

Відповіді:


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 кодує мій двовимірний масив до одновимірного масиву в json. Newtonsoft.Json.JsonConvert.SerializeObject серіалізує два виміри належним чином у json. Тому я пропоную скористатися останнім.
mono68

12
При використанні MVC 6 (можливо, також 5) вам потрібно використовувати Json.Serializeзамість кодування.
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, було б найкращим способом обернути ВСЕ віджет, який у вас є вище, так:

(
    // all js here
)();

таким чином, якщо ви розмістите кілька віджетів на сторінці, у вас не виникнуть конфлікти (якщо тільки вам не доведеться отримувати доступ до методів з інших місць сторінки, але в такому випадку вам слід все-таки зареєструвати віджет у якійсь рамці віджетів). Зараз це може не бути проблемою, але було б доцільно додати дужки зараз, щоб заощадити зусилля в майбутньому, коли це стане вимогою, також корисна практика OO для інкапсуляції функціоналу.


1
Це виглядає цікаво. Я збирався просто зробити копію даних як json і передати її як viewData, але таким чином це виглядає цікавіше. Я пограю з цим і дам вам знати. До речі, це мій перший раз, коли я розміщував запитання про stackoverflow, і це знадобило що..5 хвилин, щоб отримати хороші відповіді, що приголомшливо !!
Кріс Стівенс

Якщо не так, це просто не налаштовується, якщо ви хочете, щоб будь-яке форматування спеціальних значень ви повинні зробити це перед рукою, в основному, роблячи все рядок :( це найвизначніше з датами. Я знаю, що є легкі способи вирішення, але вони не повинні бути треба!
Andrew Bullock

@Update Я збирався ознайомитись із використанням .net статичного лічильника цього віджета для створення унікального імені об’єкта. Але те, що ви написали, виглядає так, що це здійснить би те саме, що зробить це стрункішим. Також я розглядав обов'язковість створення віджета "new_widget_event" до об'єкта на рівні сторінки, щоб відстежувати їх, але первісною причиною цього став OBE. Тому я можу переглянути це пізніше. Дякую за всю допомогу, яку я збираюся опублікувати модифіковану версію того, що ви запропонували, але поясніть, чому мені це подобається краще
Кріс Стівенс

2
я відкликаю своє попереднє твердження "нічого поганого в цьому немає". Тут все не так.
Ендрю Буллок

Чому ви кажете, що ми не можемо повернути JSON з контролера. Ось як це слід зробити. Використовуючи JavaScriptSerializerабо Return Json(object)обидва використовуйте однакові серіалізатори. Також відправлення того ж JSON назад в контролер відновить об'єкт для вас, поки ви визначите правильну модель. Можливо, під час MVC2 це був головний недолік .. але сьогодні це вітер і дуже зручно. Вам слід оновити свою відповідь, щоб це відобразити.
Пьотр Кула

18

Мені здалося, що це дуже приємно робити так (використання на виду):

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

Ось відповідний хелперний метод Клас розширення:

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());
    }
}

Це не надто м'яко, але воно вирішує проблему, куди його поставити (у контролер чи на вигляд?) Відповідь очевидно: ні;)


На мій погляд це було приємно і чисто і загорнуте в помічника для багаторазового використання. Cheers, J
Джон

6

Ви можете використовувати Jsonз дії безпосередньо,

Ваша дія буде приблизно таким:

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

Редагувати

Щойно я побачив, що ви припускаєте, що Modelце перегляд, тому вищезгадане не є строго правильним, вам доведеться Ajaxзателефонувати до методу контролера, щоб отримати це, тоді ascxб не була модель сама по собі, я залишу свій код про всяк випадок, якщо вам це корисно, і ви можете змінити дзвінок

Для редагування 2 просто введіть ідентифікатор у код


1
але він не може зробити це на вигляд, йому доведеться здійснити другий дзвінок в ефірі
Ендрю Буллок

Дякую, я спочатку робив це за допомогою виклику jQuery get json і планував, щоб HTML-елементи заповнили їх із json. Однак шаблон, який ми дотримуємося зараз, полягає в тому, що наші погляди повинні повертати modelView, і це найпростіший спосіб заповнити основні елементи HTML, наприклад таблиці тощо. Я можу надсилати ті самі дані у форматі JSON, що і ViewData, але це здається марнотратним.
Кріс Стівенс

2
ВИ НЕ слід вставляти JSON з результатом перегляду. ОП потрібно або дотримуватися стандартних MVC, що практикуються, і повертати модель або модель перегляду, або робити AJAX. Немає жодної ПРИЧИНИ вставляти JSON з видом. Це просто дуже брудний код. Ця відповідь - це вірний спосіб та рекомендований Microsoft Practices. Зниження було непотрібне, це, безумовно, правильна відповідь. Ми не повинні заохочувати погану практику кодування. Або JSON через AJAX, або Моделі через Перегляд. Ніхто не любить код спагетті зі змішаною розміткою!
Пьотр Кула

Це рішення призведе до наступної проблеми: stackoverflow.com/questions/10543953/…
Дженні О'Рейлі

2

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



0

Подовження чудової відповіді від Дейва . Ви можете створити простий HtmlHelper .

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

І на ваш погляд:

@Html.RenderAsJson(Model)

Таким чином ви можете централізувати логіку створення JSON, якщо ви з якихось причин хочете пізніше змінити логіку.


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, однак для цього потрібен один менший дзвінок, щоб заощадити накладні витрати. До речі, це давно для дат, але це схоже на іншу тему.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.