up vote 1 down vote favorite
share [g+] share [fb]

I have a json array containing integers and objects.

[1,2,3,{Name:"russia",Value:6},{Name:"usa",Value:"8"}]

I also have the following server-side class

class country {
  string Name;
  int Value;
}

How should I go about binding the json array to a server-side parameter ? I tried using a List<object> on server. It binds the integers fine but no country instances are created. Instead, primitive objects are created and added to the list.

Thanks.

link|improve this question

Is there a way to do it by creating a custom value provider ? – Amrit Jun 22 '11 at 19:54
This is especially important for passing data to Flot graphs - the entire data series is a mixed type array for which the first solution below would never work. – Chris Moschini Jul 9 '11 at 19:27
feedback

2 Answers

You could try something like this:

Format your controller action to accept a List<int> for your integer values and a List<Country> for your Country objects.

public ActionResult Index(List<int> intValues, List<Country> countryValues)

Then build your JSON like so that it contains and array of integers and an array of country objects:

var postData = {
    intValues: [1, 2, 3],
    countryValues: [
        { Name: 'USA', Value: 6 },
        { Name: 'Russia', Value: 8 }
    ]
};

And perform a simple AJAX call to send the data

$(function() {
    $.ajax({
        type: 'POST',
        url: "@Url.Action("Create")",
        contentType: "application/json",
        data: JSON.stringify(postData)
    });
});
link|improve this answer
Thanks for the reply Gram. I'm aware of this solution. I was wondering if there's a way of overriding the default model binding behavior to get the List<object>. I'm fine as long as country instances are created and upcasted to "object". – Amrit Jun 22 '11 at 18:44
feedback
up vote 1 down vote accepted

Okay, I solved it at last. I created a custom model binder derived from the default model binder to accomplish this. Here's the code.

public ActionResult Home(NonHomogeneousList input)

[ModelBinder(typeof(CustomModelBinder))]
class NonHomogeneousList : List<object>

public class CustomModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            NonHomogeneousList model = (NonHomogeneousList)base.BindModel(controllerContext, bindingContext);
            if (model.Members != null)
            {
                IModelBinder countryBinder = Binders.GetBinder(typeof(Country));

            // find out if the value provider has the required prefix
            bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
            string prefix = (hasPrefix) ? bindingContext.ModelName + "." : "";

            for (var i = 0; i < model.Count; i++)
            {
                var member = model.Members[i];
                if (member.GetType().Equals(typeof(object)))
                {
                    var subKey =  CreateSubIndexName( prefix , i);
                    ModelBindingContext innerContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Country)),
                        ModelName = subKey,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider,
                    };
                    var country = countryBinder.BindModel(controllerContext, innerContext);
                    model.Members[i] =  country;
                }
            }
        }
        return model;
    }
}
}
link|improve this answer
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.