2009-09-25 9 views
19

Pracuję na stronie internetowej, która będzie publikować obiekt JSON (przy użyciu metody jQuery Post) po stronie serwera.W środowisku ASP.NET MVC deserialize JSON przed lub w działaniu akcji kontrolera

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

W tym samym czasie napisałem klasy po stronie serwera dla tej struktury danych.

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

Po uruchomieniu witryny z następującym kodem w klasie kontrolera właściwość FullName nie jest przekształcana w postaci szeregowej. Co ja robię źle?

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

    return View(); 
} 
+3

MVC nie obsługuje JSON deserializacji out-of-the-box, ale rozważamy dodanie go do wersji 2. W międzyczasie możesz użyć JavaScriptSerializer, aby przekształcić treść żądania w w pełni nawodniony obiekt użytkownika. – Levi

+1

@Levi - to powinno być stanowisko odpowiedzi;) – womp

+0

To dziwne; w jakiś sposób właściwość ID została poprawnie zserializowana. Gdybym używał JavaScriptSerializer, czy parametrem wejściowym do Submit() byłby typ Object? – weilin8

Odpowiedz

22

Rozwiązałem mój problem, wdrażając filtr akcji; przykład kodu znajduje się poniżej. Z badań dowiedziałem się, że istnieje inne rozwiązanie, model spoiwa, opisany powyżej jako takepara. Ale tak naprawdę nie znam tych plusów i minusów robienia w obu przypadkach.

Podziękowania dla blog post Steve'a Gentile'a za to rozwiązanie.

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 z umiejętnościami MVC 133t, lepiej naucz mnie w poniedziałek;) – TJB

+3

Ta metoda ma znaczną przewagę nad niestandardowym modelemBinder, ponieważ można zdefiniować typ do deserializacji. Dzięki niestandardowemu modułowi ModelBinder jest on zakodowany na stałe i dlatego jest przydatny tylko dla jednego typu. –

+0

bardzo elastyczny, świetnie! –

5

Możesz spróbować Json.NET. Model documentation jest całkiem niezły i powinien być w stanie uzyskać do what you need. Będziesz także chciał pobrać JsonNetResult, ponieważ zwraca ActionResult, który może być użyty w aplikacji ASP.NET MVC. Jest dość łatwy w użyciu.

Json.NET działa również dobrze z serializacją dat. Więcej informacji na ten temat: can be found here.

Mam nadzieję, że to pomoże.

11

1.Create zwyczaj modelu spoiwo

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

modelu 2.Dodaj zdjęcia spoiwo w Application_Start imprezy

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

3.Use jQuery $ .get/$. Odpowiedzieć w widoku klienta kodu 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

Być może masz na myśli "ContentType" zamiast "AcceptTypes" – Dmitriy

4

Spróbuj tego;

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

Po niektórych badań, znalazłem rozwiązanie Takepara by być najlepszym rozwiązaniem dla zastąpienia domyślnego MVC JSON Deserializator z Newtonsoft za Json.NET. Może też być uogólnione do wszystkich typów w zespole, co następuje:

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

Następnie w Global.asax.cs, Application_Start():

 var asmDto = typeof(SomeDto).Assembly; 
     foreach (var t in asmDto.GetTypes()) 
     { 
      ModelBinders.Binders[t] = new MyModelBinder(); 
     } 
Powiązane problemy