Come convertire View Model in oggetto JSON in ASP.NET MVC?


156

Sono uno sviluppatore Java, nuovo su .NET. Sto lavorando a un progetto .NET MVC2 in cui voglio avere una visione parziale per avvolgere un widget. Ogni oggetto widget JavaScript ha un oggetto dati JSON che verrebbe popolato dai dati del modello. Quindi i metodi per aggiornare questi dati sono associati agli eventi quando i dati vengono modificati nel widget o se tali dati vengono modificati in un altro widget.

Il codice è qualcosa del genere:

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>

Quello che non so è come inviare i dati SomeModelViewe come poterli utilizzare per popolare il widget e convertirlo in JSON. Avevo visto alcuni modi davvero semplici per farlo nel controller ma non nella vista. Immagino che questa sia una domanda di base, ma ci vado da qualche ora cercando di rendere questo slick.


1
So che questa è una vecchia domanda. Ma ad oggi ci sono modi migliori per farlo. Non mescolare JSON in linea con il risultato della visualizzazione. JSON è facilmente serializzato tramite AJAX e può essere trattato come oggetti. Qualsiasi cosa in JavaScript dovrebbe essere separata dalla vista. Puoi facilmente restituire modelli senza alcuno sforzo tramite un controller.
Piotr Kula,

Risposte:


346

In mvc3 con il rasoio @Html.Raw(Json.Encode(object))sembra fare il trucco.


1
+1 Ho usato Html.Raw, ma non ho mai trovato Json.Encode e ho appena usato JavaScriptSerializer per aggiungere la stringa nel controller al modello di visualizzazione
AaronLS

5
Questo approccio funziona anche quando si desidera passare il JSON risultante a Javascript. Razor si lamenta con gli squiggles verdi se si inserisce il codice @ Html.Raw (...) come parametro di funzione all'interno dei tag <script>, ma il JSON lo rende effettivamente al JS chiamato. Molto maneggevole e liscio. +1
Carl Heinrich Hancke,

1
Questo è il modo per farlo da MVC3 e dovrebbe essere restituito da un controller. Non incorporare json nel tuo Viewresult. Invece, crea un controller per gestire separatamente JSON ed eseguire una richiesta JSON tramite AJAX. Se hai bisogno di JSON nella vista, stai facendo qualcosa di sbagliato. Utilizzare un ViewModel o qualcos'altro.
Piotr Kula,

3
Json.Encode codifica il mio array bidimensionale in un array monodimensionale in json. Newtonsoft.Json.JsonConvert.SerializeObject serializza correttamente le due dimensioni in json. Quindi suggerisco di usare quest'ultimo.
mono68,

12
Quando si utilizza MVC 6 (forse anche 5) è necessario utilizzare Json.Serializeinvece di Encode.
KoalaBear,

31

Ben fatto, hai appena iniziato a utilizzare MVC e hai trovato il suo primo grande difetto.

Non vuoi davvero convertirlo in JSON nella vista e non vuoi davvero convertirlo nel controller, poiché nessuna di queste posizioni ha senso. Sfortunatamente, sei bloccato con questa situazione.

La cosa migliore che ho trovato è inviare JSON alla vista in un ViewModel, in questo modo:

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

return View("viewname", viewModel);

quindi utilizzare

<%= Model.JsonData %>

a tuo avviso. Tieni presente che .NET StandardSerializer standard è piuttosto schifoso.

farlo nel controller lo rende almeno testabile (anche se non esattamente come sopra - probabilmente vorrai prendere un ISerializer come dipendenza in modo da poterlo deridere)

Aggiorna anche, per quanto riguarda il tuo JavaScript, sarebbe buona norma avvolgere TUTTI i widget JS che hai sopra in questo modo:

(
    // all js here
)();

in questo modo se metti più widget su una pagina, non otterrai conflitti (a meno che tu non abbia bisogno di accedere ai metodi da un'altra parte della pagina, ma in quel caso dovresti comunque registrare il widget con qualche framework di widget). Potrebbe non essere un problema ora, ma sarebbe una buona pratica aggiungere le parentesi ora per risparmiare molto sforzo in futuro quando diventa un requisito, è anche una buona pratica OO incapsulare la funzionalità.


1
Questo sembra interessante. Stavo per fare una copia dei dati come json e passarli come viewData, ma in questo modo sembra più interessante. Giocherò con questo e ti farò sapere. A proposito, questa è la prima volta che inserisco una domanda su StackOverflow e ci sono voluti 5 minuti per ottenere buone risposte, è fantastico !!
Chris Stephens,

nothings sbagliato con esso, il suo solo non è personalizzabile, se volete qualsiasi valore personalizzato formattazione devi farlo prima mano, in pratica rendendo tutto una stringa :( questo è più evidente con date. So che ci sono semplici soluzioni alternative, ma sognerei essere necessario!
Andrew Bullock,

@Update Stavo per vedere come usare un contatore statico .net di quel widget per generare un nome oggetto univoco. Ma quello che hai scritto sembra che realizzerebbe la stessa cosa e lo farebbe più nitido. Inoltre ho cercato di associare la creazione di un widget "new_widget_event" a un oggetto a livello di pagina per rintracciarli, ma il motivo originale per farlo è diventato OBE. Quindi potrei rivederlo più tardi. Grazie per tutto l'aiuto che ho intenzione di pubblicare una versione modificata di ciò che hai suggerito, ma metti spiegato perché mi piace di più
Chris Stephens,

2
ritiro la mia precedente dichiarazione "non c'è niente di sbagliato in essa". C'è tutto di sbagliato in questo.
Andrew Bullock,

Perché dici che non possiamo restituire JSON dal controller. È così che dovrebbe essere fatto. Utilizzando JavaScriptSerializero Return Json(object)entrambi utilizzano gli stessi serializzatori. Anche la pubblicazione dello stesso JSON sul controller ricostruirà l'oggetto per te finché definisci il modello corretto. Forse durante MVC2 è stato un grosso svantaggio .. ma oggi è un gioco da ragazzi e molto conveniente. È necessario aggiornare la risposta per riflettere questo.
Piotr Kula,

18

Ho trovato molto carino farlo in questo modo (utilizzo nella vista):

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

Ecco il metodo helper secondo Classe di estensione:

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

Non è super-sofisticato, ma risolve il problema di dove inserirlo (in Controller o in vista?) La risposta è ovviamente: nessuno dei due;)


Questo è stato bello e pulito secondo me e racchiuso in un aiuto riutilizzabile. Saluti, J
John,

6

Puoi usare Jsondirettamente dall'azione,

La tua azione sarebbe qualcosa del genere:

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

modificare

Ho appena visto che supponi che questo sia il Modeldi una Vista, quindi quanto sopra non è strettamente corretto, dovresti effettuare una Ajaxchiamata al metodo del controller per ottenere questo, ascxquindi non avrei un modello in sé, lascerò il mio codice nel caso in cui ti sia utile e puoi modificare la chiamata

Modifica 2 ha appena inserito id nel codice


1
ma non può renderlo visibile, dovrebbe fare una seconda chiamata ajax
Andrew Bullock,

Grazie, originariamente stavo facendo questo usando una chiamata jQuery get json e stavo pianificando che gli elementi HTML li popolassero da json. Tuttavia, il modello che stiamo seguendo in questo momento è che le nostre viste dovrebbero restituire un modelView e questo è il modo più semplice per popolare elementi HTML di base come tabelle ecc. Potrei inviare gli stessi dati in formato JSON di ViewData ma sembra dispendioso.
Chris Stephens,

2
NON dovresti incorporare JSON con un risultato di visualizzazione. L'OP deve attenersi allo standard MVC praticato e restituire un modello o un modello di visualizzazione o eseguire un AJAX. Non vi è assolutamente alcun motivo per incorporare JSON in vista. Questo è solo un codice molto sporco. Questa risposta è il modo corretto e il modo consigliato da Microsoft Practices. Il downvote non era necessario, questa è sicuramente la risposta corretta. Non dovremmo incoraggiare cattive pratiche di codifica. JSON tramite AJAX o Modelli tramite Views. A nessuno piace il codice spaghetti con markup misto!
Piotr Kula,

Questa soluzione provocherà il seguente problema: stackoverflow.com/questions/10543953/…
Jenny O'Reilly,

2

@ Html.Raw (Json.Encode (object)) può essere utilizzato per convertire View Modal Object in JSON


1

Estendere la grande risposta di Dave . Puoi creare un semplice HtmlHelper .

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

E a tuo avviso:

@Html.RenderAsJson(Model)

In questo modo è possibile centralizzare la logica per la creazione di JSON se, per qualche motivo, si desidera modificare la logica in un secondo momento.



0

Andrew ha avuto un'ottima risposta ma volevo modificarlo un po '. Il modo in cui è diverso è che mi piacciono le mie ModelViews per non avere dati generali in esse. Solo i dati per l'oggetto. Sembra che ViewData sia adatto ai dati generali, ma ovviamente sono nuovo in questo. Suggerisco di fare qualcosa del genere.

controllore

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

Visualizza

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

Ciò che fa per te è che ti dà gli stessi dati nel tuo JSON come nel tuo ModelView in modo da poter potenzialmente restituire il JSON al tuo controller e avrebbe tutte le parti. Questo è simile alla semplice richiesta tramite una richiesta JSON, tuttavia richiede una chiamata in meno, quindi consente di risparmiare quel sovraccarico. A proposito questo è funky per le date ma sembra un altro thread.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.