1

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; }
Jim Hewitt
  • 1,726
  • 4
  • 24
  • 26
LWarthen
  • 109
  • 2
  • 7
  • Possible duplicate of [Casting interfaces for deserialization in JSON.NET](http://stackoverflow.com/questions/5780888/casting-interfaces-for-deserialization-in-json-net) – BWA Sep 30 '16 at 14:13

4 Answers4

0

I believe the Deserializer does not know what to map the interface to so providing a bit of hint of the Deserializer would do the trick.

public class TaskDetail
{
    TaskDetails(PartPick partPick)
    {
        Behavior = partPick;
    }

    TaskDetails(TorqueTool torqueTool)
    {
        Behavior = torqueTool;
    }

    public int Id { get; set; }
    public IBehavior Behavior { get; set; }
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
0

Maybe this answer will be appropriate for you. Basically what they are proposing is to pass instances of classes that are implementing this IBehavior interface.

public class TaskDetail
{
    public TaskDetail(TorqueTool behavior){
       Behavior = behavior;
    }
    public int Id { get; set; }
    public IBehavior Behavior { get; set; }
}
Community
  • 1
  • 1
Paweł Hemperek
  • 1,130
  • 10
  • 21
0

If you are using a DataContractSerializer, you should use the "KnownTypes" Attribute to define app the types that you are expecting to implement that interface.

If you are using an XML Serializer, you will need to tag it with the "XmlInclude" attribute for each type as seen in this question.

Community
  • 1
  • 1
Theo
  • 885
  • 6
  • 16
0

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());
LWarthen
  • 109
  • 2
  • 7