2009-09-25 10 views
19

Sto lavorando su un sito Web che pubblicherà un oggetto JSON (utilizzando il metodo jQuery Post) sul lato server.In ASP.NET MVC, deserializzare JSON prima o nel metodo di azione del controllore

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

Allo stesso tempo, ho scritto classi sul lato server per questa struttura dati.

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

Quando eseguo il sito web con seguente codice nella mia classe di controllo, la proprietà FullName non viene deserializzato. Che cosa sto facendo di sbagliato?

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

    return View(); 
} 
+3

MVC non supporta JSON deserializzazione out-of-the-box, ma stiamo pensando di aggiungerlo per la v2. Nel frattempo, puoi utilizzare JavaScriptSerializer per trasformare il corpo della richiesta in un oggetto utente completamente idratato. – Levi

+1

@Levi - che dovrebbe essere un post di risposta;) – womp

+0

Questo è strano; in qualche modo la proprietà ID è stata deserializzata correttamente. Se dovessi usare JavaScriptSerializer, il parametro di input su Submit() dovrebbe essere di tipo Object? – weilin8

risposta

22

ho risolto il mio problema mediante l'attuazione di un filtro azione; codice di esempio è fornito di seguito. Dalla ricerca, ho appreso che esiste un'altra soluzione, modello legante, come takepara descritto sopra. Ma io non so davvero che pro e contro di fare in entrambi gli approcci.

Grazie a Steve Gentile blog post per questa soluzione.

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 MVC skills, è meglio che mi insegni lunedì;) – TJB

+3

Questo metodo ha un notevole vantaggio rispetto al ModelBinder personalizzato in quanto è possibile definire il tipo di deserializzazione. Con il ModelBinder personalizzato, è hardcoded e quindi utile solo per un tipo. –

+0

molto flessibile, fantastico! –

5

Si potrebbe provare Json.NET. Il documentation è abbastanza buono e dovrebbe essere in grado di do what you need. Dovrai anche prendere JsonNetResult perché restituisce un ActionResult che può essere utilizzato nell'applicazione MVC di ASP.NET. È abbastanza facile da usare.

Json.NET funziona anche bene con la serializzazione della data. Maggiori informazioni su quello can be found here.

Spero che questo aiuti.

11

modello 1.Creare personalizzato legante

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

modello 2.Add legante in Application_Start evento

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

3.use jQuery $ .get/$. Partecipa ai client di visualizzazione codice 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

Potrebbe voler dire 'ContentType' invece di 'AcceptTypes' – Dmitriy

4

Prova questo;

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

Dopo alcune ricerche, ho trovato la soluzione di Takepara di essere la migliore opzione per la sostituzione del deserializzatore di default MVC JSON con Json.NET di Newtonsoft. Può anche essere generalizzato a tutti i tipi in un assieme come segue:

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

Poi, in Global.asax.cs, Application_Start():

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