ASP.NET MVC Cómo pasar el objeto JSON de la vista al controlador como parámetro

Tengo un objeto JSON complejo que se envía a la Vista sin ningún problema (como se muestra a continuación) pero no puedo resolver cómo serializar estos datos a un objeto .NET cuando se devuelve al controlador a través de una llamada AJAX. Los detalles de las diversas partes están a continuación.

var ObjectA = { "Name": 1, "Starting": new Date(1221644506800), "Timeline": [ { "StartTime": new Date(1221644506800), "GoesFor": 200 } , { "StartTime": new Date(1221644506800), "GoesFor": 100 } ] }; 

No estoy seguro de cómo se puede pasar este objeto a un método de controlador. Tengo este método a continuación, donde el objeto Timelines refleja el objeto JS anterior utilizando Propiedades.

 public JsonResult Save(Timelines person) 

El jQuery que estoy usando es:

  var encoded = $.toJSON(SessionSchedule); $.ajax({ url: "/Timeline/Save", type: "POST", dataType: 'json', data: encoded, contentType: "application/json; charset=utf-8", beforeSend: function() { $("#saveStatus").html("Saving").show(); }, success: function(result) { alert(result.Result); $("#saveStatus").html(result.Result).show(); } }); 

He visto esta pregunta que es similar, pero no exactamente igual ya que no estoy usando formularios para manipular los datos. Cómo pasar el tipo complejo usando json al controlador ASP.NET MVC

También he visto referencias al uso de un ‘JsonFilter’ para deserializar manualmente el JSON, pero me preguntaba si hay una manera de hacerlo nativamente a través de ASP.NET MVC. ¿O cuáles son las mejores prácticas para pasar datos de esta manera?

Editar:

Este método ya no debería ser necesario con la llegada de MVC 3, ya que se manejará automáticamente – http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3 -preview-1.aspx


Puedes usar este ObjectFilter:

  public class ObjectFilter : ActionFilterAttribute { public string Param { get; set; } public Type RootType { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { if ((filterContext.HttpContext.Request.ContentType ?? string.Empty).Contains("application/json")) { object o = new DataContractJsonSerializer(RootType).ReadObject(filterContext.HttpContext.Request.InputStream); filterContext.ActionParameters[Param] = o; } } } 

A continuación, puede aplicarlo a sus métodos de control de la siguiente manera:

  [ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))] public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... } 

Entonces, básicamente, si el tipo de contenido de la publicación es “application / json”, esto entrará en acción y asignará los valores al objeto del tipo que especifique.

Usted dice “No estoy usando formularios para manipular los datos”. Pero estás haciendo un POST. Por lo tanto, está utilizando un formulario, incluso si está vacío.

El dataType de $ .ajax le dice a jQuery qué tipo devolverá el servidor, no lo que está pasando. POST solo puede pasar un formulario. jQuery convertirá los datos a pares clave / valor y lo pasará como una cadena de consulta. De los documentos:

Datos que se enviarán al servidor. Se convierte a una cadena de consulta, si aún no es una cadena. Se adjunta a la url para solicitudes GET. Vea la opción processData para evitar este procesamiento automático. El objeto debe ser pares clave / valor. Si value es una matriz, jQuery serializa múltiples valores con la misma clave, es decir, {foo: [“bar1”, “bar2”]} se convierte en ‘& foo = bar1 & foo = bar2’.

Por lo tanto:

  1. No está pasando JSON al servidor. Estás pasando JSON a jQuery.
  2. La vinculación del modelo ocurre de la misma manera que ocurre en cualquier otro caso.

Una toma diferente con un simple plugin jQuery

Aunque hacía tiempo que las respuestas a esta pregunta estaban pendientes, todavía estoy publicando una buena solución con la que vine hace un tiempo y hace que sea realmente sencillo enviar JSON complejo a las acciones del controlador Asp.net MVC para que se ajusten al modelo de lo que sea fuerte tipo de parámetros

Este complemento también es compatible con las fechas , por lo que se convierten a su contraparte de DateTime sin ningún problema.

Puede encontrar todos los detalles en la publicación de mi blog donde examino el problema y proporciono el código necesario para lograrlo.

Todo lo que tienes que hacer es usar este complemento en el lado del cliente. Una solicitud de Ajax se vería así:

 $.ajax({ type: "POST", url: "SomeURL", data: $.toDictionary(yourComplexJSONobject), success: function() { ... }, error: function() { ... } }); 

Pero esto es solo parte de todo el problema. Ahora podemos volver a enviar JSON complejo al servidor, pero dado que se vinculará al modelo con un tipo complejo que puede tener atributos de validación en las propiedades, las cosas pueden fallar en ese momento. También tengo una solución para eso . Mi solución aprovecha la funcionalidad de jQuery Ajax donde los resultados pueden ser exitosos o erróneos (tal como se muestra en el código superior). Entonces, cuando falle la validación, se llamará a la función de error como se supone que debe ser.

Existe la clase JavaScriptSerializer que puede usar también. Eso te permitirá deserializar el json a un objeto .NET. Hay un Deserialize genérico, aunque necesitará el objeto .NET para tener una firma similar a la de javascript. Además, también hay un método DeserializeObject que solo hace un object simple. Luego puede usar el reflection para obtener las propiedades que necesita.

Si su controlador toma un FormCollection , y no agregó nada más a los data el json debe estar en la form[0] :

 public ActionResult Save(FormCollection forms) { string json = forms[0]; // do your thing here. } 

Esta respuesta es un seguimiento de la respuesta de DaRKoN_ que utilizó el filtro de objetos:

 [ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))] public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... } 

Estaba teniendo problemas para averiguar cómo enviar múltiples parámetros a un método de acción y hacer que uno de ellos sea el objeto json y el otro sea una cadena simple. Soy nuevo en MVC y acababa de olvidar que ya resolví este problema con vistas que no son ajaxed.

Lo que haría si necesitara, digamos, dos objetos diferentes en una vista. Crearía una clase ViewModel. Entonces, si necesitaba el objeto de persona y el objeto de dirección, haría lo siguiente:

 public class SomeViewModel() { public Person Person { get; set; } public Address Address { get; set; } } 

Luego vincularía la vista a SomeViewModel. Puedes hacer lo mismo con JSON.

 [ObjectFilter(Param = "jsonViewModel", RootType = typeof(JsonViewModel))] // Don't forget to add the object filter class in DaRKoN_'s answer. public JsonResult doJsonStuff(JsonViewModel jsonViewModel) { Person p = jsonViewModel.Person; Address a = jsonViewModel.Address; // Do stuff jsonViewModel.Person = p; jsonViewModel.Address = a; return Json(jsonViewModel); } 

Luego, en la vista puede usar una simple llamada con JQuery como esta:

 var json = { Person: { Name: "John Doe", Sex: "Male", Age: 23 }, Address: { Street: "123 fk st.", City: "Redmond", State: "Washington" } }; $.ajax({ url: 'home/doJsonStuff', type: 'POST', contentType: 'application/json', dataType: 'json', data: JSON.stringify(json), //You'll need to reference json2.js success: function (response) { var person = response.Person; var address = response.Address; } }); 

en respuesta al comentario anterior de Dan:

Estoy usando este método para implementar lo mismo, pero por alguna razón recibo una excepción en el método ReadObject: “Esperando elemento ‘raíz’ del espacio de nombres ” .. Encontré ‘Ninguno’ con nombre ”, espacio de nombres ”. ” ¿Alguna idea de por qué? – Dan Appleyard 6 de abril de 10 a 17:57

Tuve el mismo problema (MVC 3 build 3.0.11209.0), y la publicación a continuación me la resolvió. Básicamente, el serializador json está tratando de leer una secuencia que no está al principio, por lo que reposicionando la secuencia a 0 ‘fijo’ …

http://nali.org/asp-net-mvc-expecting-element-root-from-namespace-encountered-none-with-name-namespace/