Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI

MVC Complex Model Postback: Bind Field to JSON string

5.00/5 (3 votes)
20 Jul 2013CPOL 13.7K  
MVC Complex model postback: bind field to JSON string

Introduction

In my scenario, model had both simple fields directly bindable to input controls, and an array of complex objects that was constructed using fancy JavaScript. I had to submit the whole model using one form postback (no AJAX).

This is the model (the challenge here was to bind ComplexData):

C#
public class ComplexModel
{
    public string FirstName {get;set;}
    public string LastName {get;set;}

    [JsonBindable]
    public List<ComplexObject> ComplexData {get;set;}
}

Controller is trivial:

C#
public class ComplexController : Controller
{
    public ViewResult Save(ComplexModel model)
    {
        // save it
    }
}

View binds FirstName and LastName directly to inputs and ComplexData property is prepared just before form submit by stringifying the JSON object. Formatted JSON string is placed in a hidden field.

HTML
<script>
    $(document).ready(function () {
        $('form').submit(function () {
            $('input[name=ComplexData]').val(JSON.stringify(getMyComplexDataObject()));
        });
    }
</script>

<form>
    <input name='FirstName'/>
    <input name='LastName'/>
    <input type='hidden' name='ComplexData'/>
    <input type='submit' value='Save!' />
</form>

So the problem was to bind JSON string to the ComplexObject. The solution to the problem was to create custom attribute:

C#
public class JsonBindableAttribute : Attribute
{
}

… and a custom model binder:

C#
public class JsonPropBinder : DefaultModelBinder
{
    protected override object GetPropertyValue(ControllerContext controllerContext,
    ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor,
    IModelBinder propertyBinder)
    {
        if (propertyDescriptor.Attributes.OfType<Attribute>().
        Any(x => (x is JsonBindableAttribute)))
        {
            var value = bindingContext.ValueProvider.
            GetValue(bindingContext.ModelName).AttemptedValue;
            return JsonConvert.DeserializeObject
            (value, propertyDescriptor.PropertyType);
        }
        return base.GetPropertyValue(controllerContext, bindingContext,
                              propertyDescriptor, propertyBinder);
    }
}

… and finally register the binder:

C#
protected void Application_Start()
{
    ModelBinders.Binders.Add(typeof(ComplexModel), new JsonPropBinder());
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)