views:

1276

answers:

5

I am working on a website that will post a JSON object (using jQuery Post method) to the server side.

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

At the same time, I wrote classes on the server side for this data structure.

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

When I run the website with following code in my controller class, the FullName property doesn't get deserialized. What am I doing wrong?

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

    return View();
}
+3  A: 

You could try Json.NET. The documentation is pretty good and it should be able to do what you need. You'll also want to grab JsonNetResult as it returns an ActionResult that can be used in ASP.NET MVC application. It's quite easy to use.

Json.NET also works well with Date serialization. More info regarding that can be found here.

Hope this helps.

Ryan Taylor
+5  A: 

1.create custom model binder

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

2.add model binder in application_start event

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

3.use jQuery $.get/$.post in view client JavaScript code.

  <% 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>
takepara
+2  A: 

Try this;

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

I resolved my problem by implementing an action filter; code sample is provided below. From the research, I learned that there is another solution, model binder, as takepara described above. But I don't really know that pros and cons of doing in either approach.

Thanks to Steve Gentile's blog post for this solution.

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();
}
weilin8
awww snap, weilin with 133t MVC skills, you better teach me on monday ; )
TJB
This method has a notable advantage over the custom ModelBinder in that you can define the type to deserialize to. With the custom ModelBinder, it's hard-coded and thus only useful for one type.
Daniel T.
A: 

Guys, what about error handling and putting them to ModelState?..

David