I'm running into a problem that I suspect is caused by an improper implementation of JSON.Net's TypeNameHandling on my part.
My Web API method expects a parameter of a base type BaseObj
. Clients are expected to pass in an object of a derived type (DerivedObj
for example). In order for .NET to map derived type to the base type, the global JsonFormatter is set to use TypeNameHandling.Auto
in WebApiConfig.cs. Here is a contrived example of what I'm doing.
// The base and derived types
public class BaseObj { public virtual string Text { get; set; } }
public class DerivedObj { public override string Text { get; set; } }
// The API controller
public class TestApiController : ApiController
{
public string TestMethod([FromBody]BaseObj obj) {
return ((DerivedObj)obj).Text; // ERROR: "Unable to cast object of type..."
}
}
// TypeNameHandling is configured in WebApiConfig.cs
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
...
}
}
My client uses JQuery to call the API method using the derived type (note the $type
field).
$.ajax({
url: "http://localhost/TestApi/TestMethod",
data: JSON.stringify({ $type: "MyAssembly.MyNamespace.DerivedObj, MyAssembly", Text: "hi!" }),
dataType: "json",
contentType: 'application/json',
method: "POST",
processData: false
})
.success(function (response) {
console.log(response); // response should be "hi!"...
})
.fail(function (response) {
console.error(response); // ...instead, TestMethod throws the cast exception
});
The problem is that the obj
parameter received by the API method is truly of type BaseObj
, not DerivedObj
as the client specified in $type
, thus an "Unable to cast object of type..." exception is thrown on the line return ((DerivedObj)obj).Text;
I suspect that TypeNameHandling is not working for these 2 reasons:
- This example works fine if I change the API method to expect the derived type rather than the base type:
public string TestMethod([FromBody]DerivedObj obj)
- If I change the
$type
field in this example to a bogus type, I do not get any error stating that the bogus type was not found. I would expect that to happen, though I don't know for a fact how that situation is handled by JSON.Net/Web API. (Does it allow the request even if the type is bad?)
I did verify that the TypeNameHandling
setting has been set correctly by watching GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling
in the debugger.
Is there more to be done to enable TypeNameHandling? I would prefer not to implement a custom JsonConverter if I don't need to.