2009-09-25 29 views
19

Estoy trabajando en un sitio web que publicará un objeto JSON (utilizando el método jQuery Post) en el lado del servidor.En ASP.NET MVC, deserialize JSON antes o en el método de acción del controlador

{ 
    "ID" : 1, 
    "FullName" : { 
     "FirstName" : "John", 
     "LastName" : "Smith" 
    } 
} 

Al mismo tiempo, escribí clases en el servidor para esta estructura de datos.

public class User 
{ 
    public int ID { get; set; } 
    public Name FullName { get; set;} 
} 

public class Name 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

Cuando ejecuto el sitio web con el siguiente código en mi clase de controlador, la propiedad FullName no se deserializa. ¿Qué estoy haciendo mal?

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Submit(User user) 
{ 
    // At this point, user.FullName is NULL. 

    return View(); 
} 
+3

MVC no soporta deserialización JSON fuera de la caja, pero estamos considerando agregarlo para v2. Mientras tanto, puede usar el JavaScriptSerializer para convertir el cuerpo de la solicitud en un objeto User completamente hidratado. – Levi

+1

@Levi - eso debería ser una publicación de respuesta;) – womp

+0

Eso es raro; de alguna manera, la propiedad de ID se deserializó correctamente. Si tuviera que usar JavaScriptSerializer, ¿el parámetro de entrada a Submit() sería type Object? – weilin8

Respuesta

22

Resolví mi problema implementando un filtro de acción; muestra de código se proporciona a continuación. De la investigación, aprendí que hay otra solución, carpeta de modelo, como takepara descrito anteriormente. Pero realmente no conozco los pros y los contras de hacer en ninguno de los enfoques.

Gracias a Steve Gentile's blog post por esta solución.

public class JsonFilter : ActionFilterAttribute 
    { 
     public string Parameter { get; set; } 
     public Type JsonDataType { get; set; } 

     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      if (filterContext.HttpContext.Request.ContentType.Contains("application/json")) 
      { 
       string inputContent; 
       using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream)) 
       { 
        inputContent = sr.ReadToEnd(); 
       } 

       var result = JsonConvert.DeserializeObject(inputContent, JsonDataType); 
       filterContext.ActionParameters[Parameter] = result; 
      } 
     } 
    } 

[AcceptVerbs(HttpVerbs.Post)] 
[JsonFilter(Parameter="user", JsonDataType=typeof(User))] 
public ActionResult Submit(User user) 
{ 
    // user object is deserialized properly prior to execution of Submit() function 

    return View(); 
} 
+2

awww snap, weilin con 133t de habilidades de MVC, es mejor que me enseñe el lunes;) – TJB

+3

Este método tiene una ventaja notable sobre el ModelBinder personalizado ya que puede definir el tipo para deserializar. Con el ModelBinder personalizado, está codificado y, por lo tanto, solo es útil para un tipo. –

+0

muy flexible, ¡excelente! –

5

Puede probar Json.NET. El documentation es bastante bueno y debería poder do what you need. También querrá tomar JsonNetResult ya que devuelve un ActionResult que se puede utilizar en la aplicación ASP.NET MVC. Es bastante fácil de usar.

Json.NET también funciona bien con la serialización de fechas. Más información sobre eso can be found here.

Espero que esto ayude.

11

modelo de carpeta de encargo 1.Create

public class UserModelBinder : IModelBinder 
    { 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     User model; 

     if(controllerContext.RequestContext.HttpContext.Request.AcceptTypes.Contains("application/json")) 
     { 
     var serializer = new JavaScriptSerializer(); 
     var form = controllerContext.RequestContext.HttpContext.Request.Form.ToString(); 
     model = serializer.Deserialize<User>(HttpUtility.UrlDecode(form)); 
     } 
     else 
     { 
     model = (User)ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext); 
     } 

     return model; 
    } 
    } 

modelo 2.Add aglutinante en Application_Start caso

ModelBinders.Binders[typeof(User)] = new UserModelBinder(); 

3.use jQuery $ .get/$. Publicar en el cliente de vista de código JavaScript.

<% using(Html.BeginForm("JsonData","Home",new{},FormMethod.Post, new{id="jsonform"})) { %> 

    <% = Html.TextArea("jsonarea","",new {id="jsonarea"}) %><br /> 

    <input type="button" id="getjson" value="Get Json" /> 
    <input type="button" id="postjson" value="Post Json" /> 
    <% } %> 
    <script type="text/javascript"> 
    $(function() { 
     $('#getjson').click(function() { 
     $.get($('#jsonform').attr('action'), function(data) { 
      $('#jsonarea').val(data); 
     }); 
     }); 

     $('#postjson').click(function() { 
     $.post($('#jsonform').attr('action'), $('#jsonarea').val(), function(data) { 
      alert("posted!"); 
     },"json"); 
     }); 
    }); 
    </script> 
+0

Puede ser que signifique 'ContentType' en lugar de 'AcceptTypes' – Dmitriy

4

Probar esto;

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Submit(FormCollection collection) 
{ 
    User submittedUser = JsonConvert.DeserializeObject<User>(collection["user"]); 
    return View(); 
} 
2

Después de algunas investigaciones, encontré la solución de Takepara a ser la mejor opción para reemplazar el valor por defecto deserializer MVC JSON con Json.NET de Newtonsoft. También se puede generalizar a todos los tipos en una asamblea de la siguiente manera:

using Newtonsoft.Json; 

namespace MySite.Web 
{ 
    public class MyModelBinder : IModelBinder 
    { 
     // make a new Json serializer 
     protected static JsonSerializer jsonSerializer = null; 

     static MyModelBinder() 
     { 
      JsonSerializerSettings settings = new JsonSerializerSettings(); 
      // Set custom serialization settings. 
      settings.DateTimeZoneHandling= DateTimeZoneHandling.Utc; 
      jsonSerializer = JsonSerializer.Create(settings); 
     } 

     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      object model; 

      if (bindingContext.ModelType.Assembly == "MyDtoAssembly") 
      { 
       var s = controllerContext.RequestContext.HttpContext.Request.InputStream; 
       s.Seek(0, SeekOrigin.Begin); 
       using (var sw = new StreamReader(s)) 
       { 
        model = jsonSerializer.Deserialize(sw, bindingContext.ModelType); 
       } 
      } 
      else 
      { 
       model = ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext); 
      } 
      return model; 
     } 
    } 
} 

Luego, en Global.asax.cs, Application_Start():

 var asmDto = typeof(SomeDto).Assembly; 
     foreach (var t in asmDto.GetTypes()) 
     { 
      ModelBinders.Binders[t] = new MyModelBinder(); 
     } 
Cuestiones relacionadas