LWarthen LWarthen - 1 month ago 7
JSON Question

Json deserialize a complex object with an Interface as a property

We are building a Web Api application using .Net 4.6
We are trying to JsonConvert.DeserializeObject a complex object. This object has a list of complex objects and in that object is an interface.
So we have a Task class has a list of TaskDetails and that class has a property of IBehavior

public class Task
{
public int Id { get; set; }
public int TaskTypeId { get; set; } //TYPE
public List<TaskDetail> TaskDetails { get; set; }
}

public class TaskDetail
{
public int Id { get; set; }
public IBehavior Behavior { get; set; }
}


And the IBehavior is an empty interface...

public interface IBehavior
{
}


And Concrete Behaviors are...

public class PartPick : IBehavior
{
public bool AllowMultiplePicks { get; set; }
public bool RunLightsOnly { get; set; }
public bool StandardLightMode { get; set; }

}


Or

public class TorqueTool : IBehavior
{
public short PSet { get; set; }
public short RundownsRequired { get; set; }
public int MultiSpindleMask { get; set; }
}


The JSon payload is:

{
"id": 10000,
"name": "Attach Spoiler",
"taskTypeId": 1,
"behavior": {
"pSet": 1,
"rundownsRequired": 1,
"multiSpindleMask": 4,
"multiSpindleMaskString": "0010000000000000"
}
}


I am getting the following error:


"The request is invalid.","modelState":{"task.taskDetails[0].behavior.pSet":["Could not create an instance of type Bl.Models.EPA.IBehavior. Type is an interface or abstract class and cannot be instantiated. Path 'taskDetails[0].behavior.pSet'


I have tried to create a JsonBodyModelbinder: IModelBinder
in which the method to deserialize is:

private static T DeserializeObjectFromJson(string json)
{
var binder = new TypeNameSerializationBinder("");

var obj = JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
return obj;
}


and it is wired up in the httpConfiguration:

config.Services.Insert(typeof(ModelBinderProvider), 0,
new SimpleModelBinderProvider(typeof(IBehavior), new JsonBodyModelBinder<IBehavior>()));


I have also tried to put the following attribute over the property of IBehavior in the TaskDetail.

[JsonConverter(typeof(JsonBodyModelBinder<IBehavior>))]
public IBehavior Behavior { get; set; }

Answer

It turns out that I can change the property in the TaskDetail from IBehavior to dynamic and it works. Then when I need to use the concrete type I have a factory that can return a concrete type (i.e. torqueTool). At that point I can do

JsonConvert.DeserializeObject<TorqueTool>(taskDetail.Behavior.ToString());
Comments