I’m using ASP.NET MVC2 and I have the following object structure:
public class IDealer {
string Name { get; set; }
List<IVehicle> Vehicles { get; set; }
}
public class DealerImpl {
public string Name { get; set; }
public List<IVehicle> Vehicles { get; set; }
}
public interface IVehicle {
string Type { get; }
}
public class Car : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
public class Truck : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
I have the following class as my ModelBinder which deserializes objects in my page requests:
public class JsonModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
return deserialize(controllerContext, bindingContext);
}
protected static bool IsJSONRequest(ControllerContext controllerContext) {
var contentType = controllerContext.HttpContext.Request.ContentType;
return contentType.Contains("application/json");
}
protected virtual object deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext) {
Type modelType = bindingContext.ModelMetadata.ModelType;
bool isNotConcrete = bindingContext.ModelMetadata.ModelType.IsInterface || bindingContext.ModelMetadata.ModelType.IsAbstract;
if (!IsJSONRequest(controllerContext)) {
return base.BindModel(controllerContext, bindingContext);
} else {
var request = controllerContext.HttpContext.Request;
var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();
if (isNotConcrete) {
Dictionary<string, Object> result = JsonConvert.DeserializeObject<Dictionary<string, Object>>(jsonStringData);
string type = result["Type"] as string;
modelType = Type.GetType(type + ",MyCompany.Common");
}
return JsonConvert.DeserializeObject(jsonStringData, modelType, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
}
}
}
// ASP.NET MVC Controller
protected override void Initialize(System.Web.Routing.RequestContext requestContext) {
base.Initialize(requestContext);
ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
}
[HttpPost]
public ActionResult addUpdateDealer(IDealer dealer) {
// breaks before here with the error in the comment below
}
// and in the aspx page
<script>
var model = <%= JsonConvert.SerializeObject(Model, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }) %>;
</script>
The problem I’m running into, is that when the code tries to deserialize the child list of IVehicles, it does not know which type of vehicle to instantiate. I put a property on IVehicle called “Type” which could be used to help determine which class to instantiate, but I’m not sure what/where/how to provide an override to perform this check.
Your solution is similar to what JSON.NET has built-in now, called TypeNameHandling. Here are the release notes on that.
Your JSON message will need to include a
$typeproperty, which won’t be deserialized, but will be interpreted by the deserializer as the concrete type to use.