Wie konvertiere ich das Ansichtsmodell in ein JSON-Objekt in ASP.NET MVC?

156

Ich bin ein Java-Entwickler, neu in .NET. Ich arbeite an einem .NET MVC2-Projekt, in dem ich eine Teilansicht zum Umschließen eines Widgets haben möchte. Jedes JavaScript-Widget-Objekt verfügt über ein JSON-Datenobjekt, das mit den Modelldaten gefüllt wird. Dann sind Methoden zum Aktualisieren dieser Daten an Ereignisse gebunden, wenn Daten im Widget geändert werden oder wenn diese Daten in einem anderen Widget geändert werden.

Der Code ist ungefähr so:

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>

Was ich nicht weiß, ist, wie man die Daten als sendet SomeModelViewund diese dann zum Auffüllen des Widgets sowie zum Konvertieren in JSON verwenden kann. Ich hatte einige wirklich einfache Möglichkeiten gesehen, dies in der Steuerung zu tun, aber nicht in der Ansicht. Ich denke, das ist eine grundlegende Frage, aber ich habe ein paar Stunden lang versucht, diesen Slick zu machen.

Chris Stephens
quelle
1
Ich weiß, dass dies eine alte Frage ist. Aber ab heute gibt es bessere Möglichkeiten, dies zu tun. Mischen Sie JSON nicht inline mit Ihrem View-Ergebnis. JSON ist einfach über AJAX zu serialisieren und kann wie Objekte behandelt werden. Alles in JavaScript sollte von der Ansicht getrennt sein. Sie können Modelle problemlos über einen Controller zurückgeben.
Piotr Kula

Antworten:

346

In mvc3 mit Rasiermesser @Html.Raw(Json.Encode(object))scheint der Trick zu tun.

Dave
quelle
1
+1 Ich habe Html.Raw verwendet, aber Json.Encode nie gefunden und nur JavaScriptSerializer verwendet, um die Zeichenfolge im Controller zum Ansichtsmodell
hinzuzufügen
5
Dieser Ansatz funktioniert auch dann, wenn Sie den resultierenden JSON an Javascript übergeben möchten. Razor beschwert sich mit den grünen Schnörkeln, wenn Sie den Code @ Html.Raw (...) als Funktionsparameter in <script> -Tags einfügen, aber der JSON schafft es tatsächlich zum aufgerufenen JS. Sehr praktisch und schick. +1
Carl Heinrich Hancke
1
Dies ist seit MVC3 der Fall und sollte von einem Controller zurückgegeben werden. Betten Sie json nicht in Ihr Viewresult ein. Erstellen Sie stattdessen einen Controller, der den JSON separat behandelt, und führen Sie eine JSON-Anforderung über AJAX aus. Wenn Sie JSON in der Ansicht benötigen, machen Sie etwas falsch. Verwenden Sie entweder ein ViewModel oder etwas anderes.
Piotr Kula
3
Json.Encode codiert mein zweidimensionales Array in ein eindimensionales Array in json. Newtonsoft.Json.JsonConvert.SerializeObject serialisiert die beiden Dimensionen ordnungsgemäß in json. Also schlage ich vor, letzteres zu verwenden.
Mono68
12
Wenn Sie MVC 6 (möglicherweise auch 5) verwenden, müssen Sie Json.Serializeanstelle von Encode verwenden.
KoalaBear
31

Gut gemacht, Sie haben gerade erst angefangen, MVC zu verwenden, und Sie haben den ersten großen Fehler gefunden.

Sie möchten es in der Ansicht nicht wirklich in JSON konvertieren, und Sie möchten es nicht wirklich in den Controller konvertieren, da keiner dieser Speicherorte sinnvoll ist. Leider stecken Sie in dieser Situation fest.

Das Beste, was ich gefunden habe, ist, den JSON wie folgt an die Ansicht in einem ViewModel zu senden:

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

return View("viewname", viewModel);

dann benutze

<%= Model.JsonData %>

aus Ihrer Sicht. Beachten Sie, dass der Standard .NET JavaScriptSerializer ziemlich beschissen ist.

Wenn Sie dies zumindest im Controller tun, ist es testbar (obwohl dies nicht genau wie oben beschrieben ist - Sie möchten wahrscheinlich einen ISerializer als Abhängigkeit verwenden, damit Sie ihn verspotten können).

Aktualisieren Sie auch in Bezug auf Ihr JavaScript. Es wird empfohlen, ALLE Widget-JS, die Sie oben haben, wie folgt zu verpacken:

(
    // all js here
)();

Auf diese Weise erhalten Sie keine Konflikte, wenn Sie mehrere Widgets auf einer Seite platzieren (es sei denn, Sie müssen von einer anderen Stelle auf der Seite auf die Methoden zugreifen, aber in diesem Fall sollten Sie das Widget trotzdem mit einem Widget-Framework registrieren). Es mag jetzt kein Problem sein, aber es wäre eine gute Praxis, die Klammern jetzt hinzuzufügen, um sich in Zukunft viel Mühe zu sparen, wenn dies erforderlich wird. Es ist auch eine gute OO-Praxis, die Funktionalität zu kapseln.

Andrew Bullock
quelle
1
Das sieht interessant aus. Ich wollte nur eine Kopie der Daten als json erstellen und als viewData übergeben, aber auf diese Weise sieht es interessanter aus. Ich werde damit spielen und dich wissen lassen. Übrigens, ich habe zum ersten Mal eine Frage zum Stackoverflow gestellt und es hat 5 Minuten gedauert, um gute Antworten zu erhalten. Das ist großartig!
Chris Stephens
Nichts ist falsch daran, es ist einfach nicht anpassbar. Wenn Sie eine benutzerdefinierte Wertformatierung wünschen, müssen Sie dies vorher tun und im Grunde alles zu einer Zeichenfolge machen :( Dies ist bei Datumsangaben am wichtigsten. Ich weiß, dass es einfache Problemumgehungen gibt, aber sie sollten es nicht sein notwendig!
Andrew Bullock
@Update Ich wollte einen statischen .net-Zähler dieses Widgets verwenden, um einen eindeutigen Objektnamen zu generieren. Aber was Sie geschrieben haben, sieht so aus, als würde es das Gleiche bewirken und es schlauer machen. Ich habe auch versucht, die Erstellung eines Widgets "new_widget_event" an ein Objekt auf Seitenebene zu binden, um sie zu verfolgen, aber der ursprüngliche Grund dafür war OBE. Deshalb könnte ich das später noch einmal überprüfen. Vielen Dank für die Hilfe, die ich veröffentlichen werde Eine modifizierte Version von dem, was Sie vorgeschlagen haben, aber erklären Sie, warum es mir besser gefällt.
Chris Stephens
2
Ich ziehe meine vorherige Aussage zurück "Es ist nichts falsch daran". Es ist alles falsch daran.
Andrew Bullock
Warum sagen Sie, dass wir JSON nicht vom Controller zurückgeben können? So soll es gemacht werden. Verwenden Sie JavaScriptSerializeroder Return Json(object)beide verwenden dieselben Serializer. Wenn Sie denselben JSON auch wieder an den Controller senden, wird das Objekt für Sie neu erstellt, solange Sie das richtige Modell definieren. Vielleicht war es während der MVC2 ein großer Nachteil. Aber heute ist es ein Kinderspiel und sehr praktisch. Sie sollten Ihre Antwort aktualisieren, um dies widerzuspiegeln.
Piotr Kula
18

Ich fand es ziemlich nett, es so zu machen (Verwendung in der Ansicht):

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

Hier ist die entsprechende Hilfsmethode Erweiterungsklasse:

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

Es ist nicht sehr ausgefeilt, aber es löst das Problem, wo es abgelegt werden soll (in Controller oder in Sicht?). Die Antwort lautet offensichtlich: weder; noch;)

Wolfgang
quelle
Dies war meiner Meinung nach schön und sauber und in einen wiederverwendbaren Helfer eingewickelt. Cheers, J
John
6

Sie können Jsonaus der Aktion direkt verwenden,

Ihre Aktion wäre ungefähr so:

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

Bearbeiten

Ich habe gerade gesehen, dass Sie davon ausgehen, dass es sich um Modeleine Ansicht handelt. Daher ist das oben Gesagte nicht unbedingt korrekt. Sie müssten Ajaxdie Controller-Methode aufrufen, um dies zu erhalten. ascxDann hätten Sie kein Modell an sich. Ich werde meinen Code verlassen für den Fall, dass es für Sie nützlich ist und Sie den Anruf ändern können

Bearbeiten 2 Geben Sie einfach die ID in den Code ein

Pharabus
quelle
1
aber er kann dies nicht in die Ansicht bringen, er müsste einen zweiten Ajax-Anruf tätigen
Andrew Bullock
Vielen Dank, ich habe dies ursprünglich mit einem jQuery get json-Aufruf getan und wollte, dass die HTML-Elemente sich selbst aus json füllen. Das Muster, dem wir derzeit folgen, ist jedoch, dass unsere Ansichten eine modelView zurückgeben sollten. Dies ist der einfachste Weg, um grundlegende HTML-Elemente wie Tabellen usw. zu füllen. Ich könnte dieselben Daten im JSON-Format wie ViewData senden, aber es scheint verschwenderisch.
Chris Stephens
2
Sie sollten JSON NICHT mit einem Ansichtsergebnis einbetten. Das OP muss sich entweder an die geübte Standard-MVC halten und ein Modell oder ein Ansichtsmodell zurückgeben oder eine AJAX durchführen. Es gibt absolut keinen Grund, JSON mit einer Ansicht einzubetten. Das ist nur sehr schmutziger Code. Diese Antwort ist der richtige und von Microsoft Practices empfohlene Weg. Die Ablehnung war unnötig, dies ist definitiv die richtige Antwort. Wir sollten keine schlechten Codierungspraktiken fördern. Entweder JSON über AJAX oder Modelle über Ansichten. Niemand mag Spaghetti-Code mit gemischtem Markup!
Piotr Kula
Diese Lösung führt zu folgendem Problem: stackoverflow.com/questions/10543953/…
Jenny O'Reilly
2

@ Html.Raw (Json.Encode (Objekt)) kann verwendet werden, um das View Modal Object in JSON zu konvertieren

Priya Payyavula
quelle
0

Erweiterung der großartigen Antwort von Dave . Sie können einen einfachen HtmlHelper erstellen .

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

Und aus Ihrer Sicht:

@Html.RenderAsJson(Model)

Auf diese Weise können Sie die Logik zum Erstellen des JSON zentralisieren, wenn Sie die Logik aus irgendeinem Grund später ändern möchten.

raucht
quelle
0

Andrew hatte eine großartige Antwort, aber ich wollte sie ein wenig twittern. Dies unterscheidet sich dadurch, dass ich möchte, dass meine ModelViews keine Overhead-Daten enthalten. Nur die Daten für das Objekt. Es scheint, dass ViewData die Rechnung für Overhead-Daten passt, aber natürlich bin ich neu in diesem Bereich. Ich schlage vor, so etwas zu tun.

Regler

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

Aussicht

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

Dies bedeutet für Sie, dass Sie in Ihrem JSON dieselben Daten wie in Ihrer ModelView erhalten, sodass Sie den JSON möglicherweise an Ihren Controller zurückgeben können und alle Teile enthalten. Dies ähnelt dem Anfordern über eine JSONRequest, erfordert jedoch einen Anruf weniger, sodass Sie diesen Overhead sparen. Übrigens ist das funky für Dates, aber das scheint ein anderer Thread zu sein.

Chris Stephens
quelle