Como converter o modelo de exibição em objeto JSON no asp.net MVC?

156

Sou desenvolvedor Java, novo no .NET. Estou trabalhando em um projeto .NET MVC2 em que quero ter uma exibição parcial para quebrar um widget. Cada objeto de widget JavaScript possui um objeto de dados JSON que seria preenchido pelos dados do modelo. Os métodos para atualizar esses dados são vinculados a eventos quando os dados são alterados no widget ou se esses dados são alterados em outro widget.

O código é algo como isto:

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>

O que não sei é como enviar os dados SomeModelViewe, em seguida, poder usá-los para preencher o widget e convertê-los para JSON. Eu já tinha visto algumas maneiras simples de fazê-lo no controlador, mas não na exibição. Eu acho que essa é uma pergunta básica, mas eu venho há algumas horas tentando deixar isso mais liso.

Chris Stephens
fonte
1
Eu sei que esta é uma pergunta antiga. Mas a partir de hoje existem melhores maneiras de fazê-lo. Não misture JSON inline com o resultado da visualização. JSON é facilmente serial através do AJAX e pode ser tratado como objetos. Qualquer coisa em JavaScript deve ser separada da Visualização. Você pode retornar modelos facilmente sem nenhum esforço através de um controlador.
Piotr Kula

Respostas:

346

No mvc3 com navalha @Html.Raw(Json.Encode(object))parece fazer o truque.

Dave
fonte
1
+1 eu usei Html.Raw, mas nunca encontrou Json.Encode e apenas utilizado JavaScriptSerializer para adicionar a cadeia no controlador para o modelo de vista
AaronLS
5
Essa abordagem funciona mesmo quando você deseja passar o JSON resultante para Javascript. O Razor reclama com os rabiscos verdes se você colocar o código @ Html.Raw (...) como um parâmetro de função dentro das tags <script>, mas o JSON realmente chega ao JS que está sendo chamado. Muito útil e liso. +1
Carl Heinrich Hancke
1
Essa é a maneira de fazer isso desde o MVC3 e deve ser retornada de um controlador. Não incorpore json no seu Viewresult. Em vez disso, crie um controlador para manipular o JSON separadamente e faça uma solicitação JSON via AJAX. Se você precisar do JSON na exibição, estará fazendo algo errado. Use um ViewModel ou outra coisa.
Piotr Kula
3
Json.Encode codifica minha matriz bidimensional para uma matriz unidimensional em json. Newtonsoft.Json.JsonConvert.SerializeObject serializa as duas dimensões corretamente em json. Então, sugiro usar o último.
precisa saber é
12
Ao usar o MVC 6 (talvez também 5), você precisa usar em Json.Serializevez de Codificar.
KoalaBear
31

Muito bem, você acabou de começar a usar o MVC e encontrou sua primeira grande falha.

Você realmente não deseja convertê-lo para JSON na visualização e não deseja convertê-lo no controlador, pois nenhum desses locais faz sentido. Infelizmente, você está preso a esta situação.

A melhor coisa que descobri é enviar o JSON para a visualização em um ViewModel, assim:

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

return View("viewname", viewModel);

então use

<%= Model.JsonData %>

na sua opinião. Esteja ciente de que o .NET JavaScriptSerializer padrão é uma porcaria.

fazê-lo no controlador pelo menos o torna testável (embora não seja exatamente como o descrito acima - você provavelmente quer usar um ISerializer como uma dependência para poder zombar dele)

Atualize também, em relação ao seu JavaScript, seria uma boa prática agrupar TODOS os JS de widget que você possui acima, desta forma:

(
    // all js here
)();

dessa maneira, se você colocar vários widgets em uma página, não terá conflitos (a menos que precise acessar os métodos de outros lugares da página, mas nesse caso, você deverá registrar o widget com alguma estrutura de widget). Pode não ser um problema agora, mas seria uma boa prática adicionar os colchetes agora para economizar muito esforço no futuro, quando isso se tornar um requisito, também é uma boa prática OO encapsular a funcionalidade.

Andrew Bullock
fonte
1
Isso parece interessante. Eu ia apenas fazer uma cópia dos dados como json e passá-los como viewData, mas dessa maneira parece mais interessante. Eu vou brincar com isso e deixar você saber. BTW, esta é minha primeira vez postando uma pergunta no stackoverflow e demorou o que ... 5 minutos para obter boas respostas, isso é incrível!
31710 Chris Stephens
nada de errado com ele, não é apenas personalizável, se você quiser alguma formatação de valor personalizada, faça isso antes da mão, basicamente transformando tudo em uma string :( isso é mais proeminente com as datas. eu sei que existem soluções fáceis, mas elas não devem ser necessário!
Andrew Bullock
@ Update Eu ia ver sobre o uso de um contador estático .net desse widget para gerar um nome de objeto exclusivo. Mas o que você escreveu parece que conseguiria a mesma coisa e o tornaria mais lógico. Também procurei vincular a criação de um widget "new_widget_event" a um objeto no nível da página para rastreá-los, mas o motivo original para isso se tornou OBE. Portanto, eu poderia revisar isso mais tarde. Obrigado por toda a ajuda que vou postar uma versão modificada do que você sugeriu, mas ponha explique por que eu gosto mais
Chris Stephens
2
retiro minha afirmação anterior "não há nada errado com ela". Há tudo errado nisso.
Andrew Bullock
Por que você diz que não podemos retornar JSON do controlador. É assim que deve ser feito. Usando JavaScriptSerializerou Return Json(object)ambos usam os mesmos serializadores. A postagem do mesmo JSON de volta ao controlador reconstruirá o objeto para você, desde que você defina o modelo correto. Talvez durante o MVC2 tenha sido uma grande desvantagem .. mas hoje é muito fácil e conveniente. Você deve atualizar sua resposta para refletir isso.
Piotr Kula
18

Achei muito legal fazer assim (uso na exibição):

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

Aqui está o método auxiliar de acordo com a classe 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());
    }
}

Não é super sofisticado, mas resolve o problema de onde colocá-lo (no Controller ou no modo de exibição?) A resposta é obviamente: nenhum;)

Wolfgang
fonte
Isso foi agradável e limpo na minha opinião e embrulhado em um ajudante reutilizável. Cheers, J
John
6

Você pode usar Json diretamente da ação,

Sua ação seria algo como isto:

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

Editar

Acabei de ver que você supõe que esta é Modeluma Visualização, portanto, o que foi exposto acima não é estritamente correto, você teria que fazer uma Ajaxchamada para o método do controlador para obter isso.ascx que foi dito pois não teria um modelo em si, deixarei meu código caso seja útil e você possa alterar a chamada

Editar 2 basta colocar o código no código

Pharabus
fonte
1
mas ele não pode tornar este para a vista, ele teria que fazer uma 2ª chamada ajax
Andrew Bullock
Obrigado, eu estava originalmente fazendo isso usando uma chamada jQuery get json e estava pensando em fazer com que os elementos HTML os preenchessem a partir do json. No entanto, o padrão que estamos seguindo agora é que nossas visualizações retornem um modelView e essa é a maneira mais fácil de preencher elementos HTML básicos como tabelas etc. Eu poderia enviar os mesmos dados no formato JSON que o ViewData, mas isso parece um desperdício.
Chris Stephens
2
Você NÃO deve incorporar JSON com um resultado de exibição. O OP precisa seguir o padrão MVC praticado e retornar um modelo ou modelo de exibição ou executar um AJAX. Não há absolutamente motivo para incorporar o JSON com uma visualização. Esse é apenas um código muito sujo. Esta resposta é a maneira correta e a recomendada pelas práticas Microsoft. O voto negativo foi desnecessário, esta é definitivamente a resposta correta. Não devemos incentivar práticas de codificação ruins. JSON via AJAX ou Models via Views. Ninguém gosta de código de espaguete com marcação mista!
Piotr Kula
Esta solução resultará no seguinte problema: stackoverflow.com/questions/10543953/…
Jenny O'Reilly
2

@ Html.Raw (Json.Encode (object)) pode ser usado para converter o objeto modal de exibição em JSON

Priya Payyavula
fonte
0

Estendendo a ótima resposta de Dave . Você pode criar um HtmlHelper simples .

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

E na sua opinião:

@Html.RenderAsJson(Model)

Dessa forma, você pode centralizar a lógica para criar o JSON se, por algum motivo, desejar alterar a lógica posteriormente.

smoksnes
fonte
0

O Andrew teve uma ótima resposta, mas eu queria mexer um pouco. A maneira como isso é diferente é que eu gosto que meus ModelViews não tenham dados gerais. Apenas os dados para o objeto. Parece que o ViewData se encaixa na conta dos dados gerais, mas é claro que sou novo nisso. Eu sugiro fazer algo assim.

Controlador

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

Visão

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

O que isso faz por você é que ele fornece os mesmos dados em seu JSON e em seu ModelView, para que você possa retornar o JSON de volta ao seu controlador e ele tenha todas as partes. Isso é semelhante a apenas solicitá-lo por meio de um JSONRequest, no entanto, requer menos uma chamada, para que você economize essa sobrecarga. BTW isso é descolado para datas, mas que parece ser outro segmento.

Chris Stephens
fonte