Jimmy Chandra
Jimmy Chandra

Reputation: 6580

How to properly model bind json with sub-objects to ASP.NET MVC ViewModel?

Given the following client side code fragment:

var vm = {
  Input : "Label: Value",
  Rules : [
    { Name : "RemoveString",
      Params : [
        "Label: "
      ]
    }
  ]
};

$.post("/API/ApplyRule", vm, function(data) { });

And the following ViewModel on server side:

[Serializable]
public class ApplyRuleRequestViewModel
{
    public string Input { get; set; }
    public List<RuleViewModel> Rules { get; set; }
}

[Serializable]
public class RuleViewModel
{
    public string Name { get; set; }
    public List<string> Params { get; set; }
}

And the following controller code:

public class APIController : Controller
{
    [HttpPost]
    public ActionResult ApplyRule(ApplyRuleRequestViewModel model)
    {
        //Problem here... model is not fully deserialized into the ViewModel object.
        return View();
    }
}

I am having a problem trying to serialize the Rules portion of the client side ViewModel. When debugging the code at the controller line above that said //Problem..., I see that the top level object properties made it, but not the sub-object. So, I get something like:

var vm = new ApplyRuleRequestViewModel {
  Input = "Label: Value",
  Rules = new List<RuleViewModel> {
     new RuleViewModel { Name = null, Parameters = null }
  }
}

I am expecting something like:

var vm = new ApplyRuleRequestViewModel {
  Input = "Label: Value",
  Rules = new List<RuleViewModel> {
     new RuleViewModel { 
         Name = "RemoveString", 
         Parameters = new List<string> { "Label: " }
     }
  }
}

What am I doing wrong here? Why is it not binding the Rules array properly?

Do you need to create your own custom model binder to bind this properly? If so, how?

Upvotes: 1

Views: 1652

Answers (1)

moribvndvs
moribvndvs

Reputation: 42497

You can send your message as JSON.

var vm = {
  Input : "Label: Value",
  Rules : [
    { Name : "RemoveString",
      Params : [
        "Label: "
      ]
    }
  ]
};

$.postJson("/API/ApplyRule", vm, function(data) { }); // See below for definition of `.postJson`.

The last argument json will set the accepts header to indicate JSON is wanted. The default model binder should automatically interact with the built-in JsonValueProviderFactory to properly read the structured message.

EDIT Missed something. You need to set the contentType, so .post as it stands may not work.

Here's a helper method for posting JSON (not just POSTING and receiving json, as post would do).

$.postJson = function(url, data, success) {
  $.ajax({
            url: url,
            type: 'POST',
            dataType: 'json',
            data: JSON.stringify(data),
            contentType: 'application/json; charset=utf-8',
            success: success
        }); 
}

Upvotes: 1

Related Questions