Skraps
Skraps

Reputation: 3

Issues binding complex json model in MVC3 controller

I have been struggling to get this post to work in my project. Bascially, I am passing a list of Install objects ( an install object contains a list of Facility objects) to my controller. I have yet to get the binding to work successfully. I have been able to get the count of list items to reflect the number of items passed to the controller, however none of the properties are being set in the individual objects.

Here is the code that performs the post:

$("#OpenFacilityResults").button().click(function () {
  var installHolder = {
    InstallList: []
  }
  var foundInstall = false
  $('.FullExport').each(function () {
    //var Install = { InstallRecnum: 0, FacilityList: [Facility, Facility] }
    //var Facility = { ID: 0, Product: 0 }
    if ($(this).prop('checked')) {
      var substr = $(this).parent().find('input:hidden').val().split('|');
      var fac = {
        ID: substr[1],
        Product: substr[2]
      }
      foundInstall = false
      $.each(installHolder.InstallList, function (index, value) {
        if (value.InstallRecnum == substr[0]) {
          foundInstall = true
          installHolder.InstallList[index].FacilityList.push(fac);
        }
      });
      if (foundInstall == false) {
        var i = {
          InstallRecnum: substr[0],
          FacilityList: []
        }
        i.FacilityList.push(fac)
        installHolder.InstallList.push(i)
      }
    }
  });
  console.log(JSON.stringify(installHolder));
  var dataHolder = JSON.stringify(installHolder)
  $.ajax({
    url: '@Url.Action("ViewFacilityDetails", "CHI")',
    type: 'POST',
    dataType: 'json',
    data: dataHolder,
    contentType: 'application/json',
    success: function (data) {
      // get the result and do some magic with it
    }
  });
});

Here is what the JSON looks like:

{"InstallList":[{"InstallRecnum":"140","FacilityList":[{"ID":"0","Product":"1"}]},{"InstallRecnum":"138","FacilityList":[{"ID":"0","Product":"1"}]}]}

Here is the controller action:

Function ViewFacilityDetails(ByVal InstallList As List(Of InstallInputModel)) As ActionResult
    Return Json(InstallList)
End Function

And the objects that I am attempting to bind:

<Serializable()> _
Public Class InstallInputModel
    Public InstallRecnum As Integer
    Public FacilityList As List(Of FacilityInputModel)
End Class
<Serializable()> _
Public Class FacilityInputModel
    Public ID As Integer
    Public Product As Integer
End Class

Any help would be much appreciated - thanks in advance!

UPDATE

Here is the working code to accomplish this:

Custom model binder:

Public Class JsonBinder
    Implements IModelBinder

    Public Function BindModel(controllerContext As System.Web.Mvc.ControllerContext, bindingContext As System.Web.Mvc.ModelBindingContext) As Object Implements System.Web.Mvc.IModelBinder.BindModel
        If controllerContext Is Nothing Then
            Throw New ArgumentNullException
        End If
        If bindingContext Is Nothing Then
            Throw New ArgumentNullException
        End If

        Dim request = controllerContext.HttpContext.Request

        request.InputStream.Seek(0, IO.SeekOrigin.Begin)
        Dim jsonString = New StreamReader(request.InputStream).ReadToEnd

        If Not jsonString Is Nothing Then

            Dim deserializer As New System.Web.Script.Serialization.JavaScriptSerializer
            Dim result = deserializer.Deserialize(Of InstallInputModelHolder)(jsonString)

            Return result
        Else
            Return Nothing
        End If

    End Function
End Class

*Gloabl.asax.vb addition *

ModelBinders.Binders.Add(GetType(InstallInputModelHolder), New JsonBinder())

I'll most likely change the JSON so it can just pass the array and an extra container class won't be needed to deserialize the JSON.

Upvotes: 0

Views: 369

Answers (1)

rivarolle
rivarolle

Reputation: 1538

Create a ModelBinder that will bind your Jsonstring to an List(of InstallInputModel) and register it in global.asax

Off the top of my head (C#):

 public class JsonBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        //return base.BindModel(controllerContext, bindingContext);
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }


        // Get the JSON data that's been posted
        var request = controllerContext.HttpContext.Request;

        request.InputStream.Seek(0, SeekOrigin.Begin);
        var jsonString = new StreamReader(request.InputStream).ReadToEnd();


        if (jsonString != null)
        {
            var serializer = new JavaScriptSerializer();
            var result = serializer.Deserialize(jsonString, typeof(List<InstallInputModel>));
            return result;

        }
        else
        {
            return null;
        }


    }
}

In the Application_Start of Global.asax, add:

ModelBinders.Binders.Add(typeof(List<InstallInputModel>), new JsonBinder());

Upvotes: 1

Related Questions