¿Cómo convertir el modelo de vista en objeto JSON en ASP.NET MVC?

156

Soy un desarrollador de Java, nuevo en .NET. Estoy trabajando en un proyecto .NET MVC2 donde quiero tener una vista parcial para ajustar un widget. Cada objeto de widget de JavaScript tiene un objeto de datos JSON que se completará con los datos del modelo. Luego, los métodos para actualizar estos datos están vinculados a eventos cuando los datos se cambian en el widget o si esos datos se cambian en otro widget.

El código es algo como esto:

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>

Lo que no sé es cómo enviar los datos como SomeModelViewy luego poder usarlos para completar el widget y convertirlos a JSON. Había visto algunas formas simples de hacerlo en el controlador, pero no en la vista. Me imagino que esta es una pregunta básica, pero he estado yendo por unas horas tratando de hacer esto ingenioso.

Chris Stephens
fuente
1
Sé que esta es una vieja pregunta. Pero a partir de hoy hay mejores formas de hacerlo. No mezcle JSON en línea con su resultado de Vista. JSON es fácilmente seriales a través de AJAX y puede tratarse como objetos. Cualquier cosa en JavaScript debe estar separada de la Vista. Puede devolver modelos fácilmente sin ningún esfuerzo a través de un controlador.
Piotr Kula

Respuestas:

346

En mvc3 con navaja @Html.Raw(Json.Encode(object))parece hacer el truco.

Dave
fuente
1
+1 Usé Html.Raw, pero nunca encontré Json.Encode y solo usé JavaScriptSerializer para agregar la cadena en el controlador al modelo de vista
AaronLS
55
Este enfoque funciona incluso cuando desea pasar el JSON resultante a Javascript. Razor se queja con los garabatos verdes si coloca el código @ Html.Raw (...) como un parámetro de función dentro de las etiquetas <script>, pero el JSON realmente llega al JS que se está llamando. Muy práctico y pulido. +1
Carl Heinrich Hancke
1
Esta es la forma de hacerlo desde MVC3 y debe devolverse desde un controlador. No incruste json en su Viewresult. En su lugar, haga un controlador para manejar el JSON por separado y haga una solicitud JSON a través de AJAX. Si necesita JSON en la vista, está haciendo algo mal. Utilice un ViewModel u otra cosa.
Piotr Kula
3
Json.Encode codifica mi matriz bidimensional en una matriz unidimensional en json. Newtonsoft.Json.JsonConvert.SerializeObject serializa las dos dimensiones correctamente en json. Así que sugiero usar el último.
mono68
12
Cuando use MVC 6 (quizás también 5) debe usar en Json.Serializelugar de Encode.
KoalaBear
31

Bien hecho, acabas de comenzar a usar MVC y has encontrado su primer defecto importante.

Realmente no desea convertirlo a JSON en la vista, y realmente no desea convertirlo en el controlador, ya que ninguna de estas ubicaciones tiene sentido. Desafortunadamente, estás atrapado con esta situación.

Lo mejor que he encontrado es enviar el JSON a la vista en un ViewModel, así:

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

return View("viewname", viewModel);

luego usa

<%= Model.JsonData %>

en tu opinión Tenga en cuenta que el .NET JavaScriptSerializer estándar es bastante basura.

hacerlo en el controlador al menos lo hace comprobable (aunque no exactamente como lo anterior, probablemente desee tomar un ISerializer como una dependencia para que pueda burlarse de él)

Actualice también, con respecto a su JavaScript, sería una buena práctica envolver TODO el widget JS que tiene arriba de la siguiente manera:

(
    // all js here
)();

de esta manera, si coloca múltiples widgets en una página, no obtendrá conflictos (a menos que necesite acceder a los métodos desde otra parte de la página, pero en ese caso debería registrar el widget con algún marco de widget de todos modos). Puede que ahora no sea un problema, pero sería una buena práctica agregar los corchetes ahora para ahorrarse muchos esfuerzos en el futuro cuando se convierta en un requisito, también es una buena práctica de OO encapsular la funcionalidad.

Andrew Bullock
fuente
1
Esto se ve interesante. Iba a hacer una copia de los datos como json y pasarla como viewData pero de esta manera se ve más interesante. Jugaré con esto y te lo haré saber. Por cierto, esta es la primera vez que publico una pregunta en stackoverflow y me llevó unos 5 minutos obtener buenas respuestas, ¡eso es increíble!
Chris Stephens
no tiene nada de malo , simplemente no es personalizable, si desea un formato de valor personalizado, debe hacerlo de antemano, básicamente haciendo que todo sea una cadena :( esto es más prominente con las fechas. Sé que hay soluciones fáciles, pero no deberían serlo necesario!
Andrew Bullock
@Update que iba a ver sobre el uso de un contador estático .net de ese widget para generar un nombre de objeto único. Pero parece que lo que escribiste lograría lo mismo y lo haría más ingenioso. También busqué vincular la creación de un widget "new_widget_event" a un objeto de nivel de página para rastrearlos, pero la razón original para hacerlo se convirtió en OBE. Así que podría volver a visitarlo más tarde. Gracias por toda la ayuda que voy a publicar Una versión modificada de lo que sugirió, pero explique por qué me gusta más.
Chris Stephens
2
Retracto mi declaración anterior "no hay nada malo en ello". Todo tiene de malo.
Andrew Bullock
¿Por qué dice que no podemos devolver JSON desde el controlador? Así es como se supone que debe hacerse. Usando JavaScriptSerializero Return Json(object)ambos usan los mismos serializadores. Publicar también el mismo JSON en el controlador reconstruirá el objeto para usted siempre que defina el modelo correcto. Quizás durante MVC2 fue un gran inconveniente ... pero hoy es muy sencillo y muy conveniente. Debería actualizar su respuesta para reflejar esto.
Piotr Kula
18

Me pareció bastante agradable hacerlo así (uso en la vista):

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

Aquí está la clase de extensión del método auxiliar correspondiente:

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

No es súper sofisticado, pero resuelve el problema de dónde colocarlo (¿en el controlador o en la vista?) La respuesta es obviamente: ninguno;)

Wolfgang
fuente
Esto fue agradable y limpio en mi opinión y envuelto en un ayudante reutilizable. Saludos, J
John
6

Puedes usar Jsondirectamente desde la acción,

Tu acción sería algo como esto:

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

Editar

Acabo de ver que asumes que esto es Modelde una Vista, por lo que lo anterior no es estrictamente correcto, tendrías que Ajaxllamar al método del controlador para obtener esto, ascxentonces no tendría un modelo per se, dejaré mi código en caso de que te sea útil y puedas modificar la llamada

Edit 2 acaba de poner id en el código

Pharabus
fuente
1
pero no puede mostrar esto a la vista, tendría que hacer una segunda llamada ajax
Andrew Bullock
Gracias, originalmente estaba haciendo esto usando una llamada jQuery get json y estaba planeando que los elementos HTML se llenen ellos mismos de json. Sin embargo, el patrón que estamos siguiendo en este momento es que nuestras vistas deberían devolver un modelView y esa es la forma más fácil de llenar elementos HTML básicos como tablas, etc. Podría enviar los mismos datos en formato JSON que ViewData pero parece un desperdicio.
Chris Stephens
2
NO debe incrustar JSON con un resultado de vista. El OP debe seguir el estándar MVC practicado y devolver un modelo o modelo de vista o hacer un AJAX. No hay absolutamente ninguna razón para incrustar JSON con una vista. Eso es solo un código muy sucio. Esta respuesta es la forma correcta y la recomendada por Microsoft Practices. El voto negativo fue innecesario, esta es definitivamente la respuesta correcta. No debemos alentar las malas prácticas de codificación. Ya sea JSON a través de AJAX o Modelos a través de Vistas. ¡A nadie le gusta el código de espagueti con marcado mixto!
Piotr Kula
Esta solución dará como resultado el siguiente problema: stackoverflow.com/questions/10543953/…
Jenny O'Reilly
2

@ Html.Raw (Json.Encode (objeto)) se puede usar para convertir el objeto modal Ver a JSON

Priya Payyavula
fuente
1

Extendiendo la gran respuesta de Dave . Puede crear un HtmlHelper simple .

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

Y en tu opinión:

@Html.RenderAsJson(Model)

De esta forma, puede centralizar la lógica para crear el JSON si, por alguna razón, desea cambiar la lógica más adelante.

smoksnes
fuente
0

Andrew tuvo una gran respuesta, pero quería modificarla un poco. La forma en que esto es diferente es que me gusta que mis ModelViews no tengan datos generales. Solo los datos para el objeto. Parece que ViewData se ajusta a la factura de los datos generales, pero, por supuesto, soy nuevo en esto. Sugiero hacer algo como esto.

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

Ver

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

Lo que esto hace por usted es que le brinda los mismos datos en su JSON que en su ModelView para que pueda devolver el JSON a su controlador y tendría todas las partes. Esto es similar a solo solicitarlo a través de JSONRequest, sin embargo, requiere una llamada menos, por lo que le ahorra esa sobrecarga. Por cierto, esto es funky para Dates pero parece otro hilo.

Chris Stephens
fuente