Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type is an interface or abstract class and cannot be instantiated

Tags:

json

c#

json.net

I will preface this by saying that I know what the problem is, I just don't know how to solve it. I am communicating with a .NET SOA data layer that returns data as JSON. One such method returns an object that has several collections within it. The object basically looks like this:

{
  "Name":"foo",
  "widgetCollection":[{"name","foo"}, {"name","foo"},],
  "cogCollection": [{"name","foo"}, {"childCogs",<<new collection>>},],
}

My class that represents this object looks like this:

public class SuperWidget : IWidget
{
    public string Name { get; set; }

    public ICollection<IWidget> WidgetCollection { get; set; }
    public ICollection<ICog> CogCollection { get; set; }

    public SuperWidget()
    {
    }

    [JsonConstructor]
    public SuperWidget(IEnumerable<Widget> widgets, IEnumerable<Cog> cogs)
    {
        WidgetCollection = new Collection<IWidget>();
        CogCollection = new Collection<ICog>();

        foreach (var w in widgets)
        {
            WidgetCollection.Add(w);
        }
        foreach (var c in cogs)
        {
            CogCollection.Add(c);
        }
    }
}

This constructor worked fine until the cogCollection added a child collection, and now I am getting the above error. A concrete cog class looks like this:

[Serializable]
public class Cog : ICog
{
    public string name { get; set; }

    public ICollection<ICog> childCogs { get; set; }        
}  

I don't want to change the collection to a concrete type because I am using IoC. Because I am using IoC I would really like to get away from the need to have the JsonConstructors that take concrete parameters, but I haven't figured out a way to do that. Any advice would be greatly appreciated!

Update:

Yuval Itzchakov's suggestion that this question is probably a duplicate is somewhat true (it seems). In the post referenced, one of the answers down the page provides same solution that was provided here. I didn't notice that answer, since the OP's question was different then the one I had here. My mistake.

-------my solution--------

As I said: Matt's solution took a little bit of work but I got something setup that works for me. The one thing I didn't like about his initial solution, were lines like these:

 return objectType == typeof(ICog); 

Following this pattern you would need to have a JsonConverter for every abstract type that you receive over the wire. This is less than ideal in my situation, so I created a generic JsonConverter as such:

public class GenericJsonConverter<T>: JsonConverter, IBaseJsonConverter<T>
{
    private readonly IUnityContainer Container;
    public GenericJsonConverter(IUnityContainer container)
    {
        Container = container;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
        var result = Container.Resolve<T>();
        serializer.Populate(target.CreateReader(), result);
        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

Then just before I deserialize my data, I do something like this:

 var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
 settings.Converters.Add((JsonConverter)Container.Resolve<IBaseJsonConverter<ICog>>());

Protip: If you use Resharper, (JsonConverter) will give you a suspicious cast warning in this scenario.

Hopefully someone else finds this useful down the road!

like image 907
dparsons Avatar asked Jul 09 '14 02:07

dparsons


People also ask

Is an interface or abstract class and Cannot be instantiated?

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.

Which are true of an interface Cannot be instantiated?

An interface can't be instantiated directly. Its members are implemented by any class or struct that implements the interface. A class or struct can implement multiple interfaces. A class can inherit a base class and also implement one or more interfaces.

Is a class that Cannot be instantiated?

An abstract class cannot be instantiated. The purpose of an abstract class is to provide a common definition of a base class that multiple derived classes can share.

Why interface is not instantiated?

You can't instantiate an interface or an abstract class because it would defy the object oriented model. Interfaces represent contracts - the promise that the implementer of an interface will be able to do all these things, fulfill the contract.


1 Answers

You'll need to provide a custom serializer to Json.Net to tell it how to handle the child cogs. For example:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new CogConverter());

Your CogConverter will need to inherit from JsonConverter and specify that it CanConvert your ICog interface. Perhaps something along the lines of:

public class CogConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ICog);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, typeof(Cog));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

I'd recommend registering your JsonSerializerSettings with your IoC container in that case. You may want to consider giving CogConverter access to the container, too, if the serializer can't be responsible for actually constructing the Cog itself; that all depends on your particular architecture.

Edit

Upon further reading, it seems like you might be looking for specifically how to use the IoC created ICog for population. I'm using the following as part of my ReadJson:

var target = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
var objectType = DetermineConcreteType(target);
var result = iocContainer.Resolve(objectType);
serializer.Populate(target.CreateReader(), result);
return result;

This allows you to use ANY object and populate it from the original JSON, using custom types as you wish inside your DetermineConcreteType method.

like image 154
Matt DeKrey Avatar answered Oct 10 '22 22:10

Matt DeKrey