Deserializing Child Interface with Json.NET
.NETProblem
When I was recently trying to deserialize some json to a type that contained an interface, I encountered this error:
Could not create an instance of type Foo. Type is an interface or abstract class and cannot be instantiated. Path ‘Bar’
I sat there for a few seconds scratching my head, when it dawned on me that of course it could not deserialize. Firstly because you can’t create a new instance of an interface however hard you tried, so how was I expecting Json.NET to. Secondly Json.NET had no idea what type to actually define it as…
Solution
So my investigation began and I discovered CustomCreationConverter which allows you to define how you want a specified type to be converted by Json.NET.
public class ResultDataConverter : CustomCreationConverter<IResultData>
{
public Type ResultDataType { get; private set; }
public ResultDataConverter(Type reportDataType)
{
// Sets the strong type for IResultData
ResultDataType = reportDataType;
}
public override IResultData Create(Type objectType)
{
// Converts the anonymouse IResultData to the strong type defined in the constructor
return (IResultData)Activator.CreateInstance(ResultDataType);
}
}
When I initialize the converter I specify how the interface will be handled.
var converter = new ResultDataConverter(typeof(ResultData));
var deseralizeSettings = new JsonSerializerSettings();
deseralizeSettings.Converters.Add(converter);
var results = JsonConvert.DeserializeObject<ResultWrapper>(json, deseralizeSettings);
I’ve committed the sample code and some unit tests to github.
Caveat
This approach worked great for me because I was consuming json from an API and the type was always the same depending on which API I called. If you need the interface to deserialize different types, this approach probably wont work for you.
If you have control over how your json is returned, then I would suggest trying to include the definition of the type within it. This means there would be an extra $type property against each object in the json.
...
'ResultType': [{
'$type': 'Foo.Bar.FoodResultType, Foo.Bar',
'Id': '1',
...
}, {
'$type': 'Foo.Bar.DrinkResultType, Foo.Bar',
'Id': '1',
...
}
This link provides some better details. SerializeTypeNameHandling