5

I have some json that I want to Deserialise. It's very easy to deserialise back to an List However I am working with interfaces, and this is what I need the deserialisation to end up at:

So Let's start with some JSON:

[{"TimeGenerated":"2017-05-30T19:21:06.5331472+10:00","OuterValue":1.08,"Identifier":{"IdentifierName":"BLA","IdentifierNumber":1},"Locale":{"LocaleName":"LOCALE NAME","LocaleType":1,"LocaleNumber":1,"StartTime":"2017-05-30T19:20:00+10:00"},"InnerValue":1.08,"InnerType":0,"InnerId":"InnerI","Type":"MyType","Id":"11111"}]

This is the data that is Serialised on the server side (without issue of course) and it's object type is :

IList<IRootObject>

So effectively, I want to deserialise the other side as:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

Obviously the pickle here is the old interface/abstract type error of:

'Could not create an instance of type BLA. Type is an interface or abstract class and cannot be instantiated'

Fine, I create some custom converters and decorate as follows:

Classes are:

    [JsonObject(MemberSerialization.OptIn)]
    public class Identifier
    {
        [JsonProperty]
        public string IdentifierName { get; set; }
        [JsonProperty]
        public int IdentifierNumber { get; set; }
    }
    [JsonObject(MemberSerialization.OptIn)]
    public class Locale
    {
        [JsonProperty]
        public string LocaleName { get; set; }
        [JsonProperty]            
        public int LocaleType { get; set; }
        [JsonProperty]            
        public int LocaleNumber { get; set; }
        [JsonProperty]
        public string StartTime { get; set; }
    }

    [JsonObject(MemberSerialization.OptIn)]
    public class RootObject:IRootObject
    {
       [JsonProperty]
       public string TimeGenerated { get; set; }
       [JsonProperty]        
       public double OuterValue { get; set; }
       [JsonProperty] 
       [JsonConverter(typeof(ConcreteConverter<Identifier>))]
       public IIdentifier Identifier { get; set; }
       [JsonProperty]
       [JsonConverter(typeof(ConcreteConverter<Locale>))]
       public ILocale Locale { get; set; }
       [JsonProperty]
       public double InnerValue { get; set; }
       [JsonProperty]
       public int InnerType { get; set; }
       [JsonProperty]
       public string InnerId { get; set; }
       [JsonProperty]
       public string Type { get; set; }
       [JsonProperty]
       public string Id { get; set; }
    }

Interface is:

public interface IRootObject
{
    string TimeGenerated { get; set; }
    double OuterValue { get; set; }
    Identifier Identifier { get; set; }
    Locale Locale { get; set; }
    double InnerValue { get; set; }
    int InnerType { get; set; }
    string InnerId { get; set; }
    string Type { get; set; }
    string Id { get; set; }
}

Concrete Converter is as follows:

public class ConcreteConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    try
    {
        var s = serializer.Deserialize<T>(reader);
        return s;
    }
    catch (Exception ex)
    {
        return null;
    }
    // return serializer.Deserialize<T>(reader);
}

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

}

I also have a list converter which I have made (seeing this is a list albeit this is unused, but here JIC it is needed):

public class ConcreteListConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (existingValue == null)
        return null;

    var res = serializer.Deserialize<List<TImplementation>>(reader);
    return res.ConvertAll(x => (TInterface)x);
}

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

}

}

So the issues are:

When I run:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

I always get a null returned.

When i put breakpoints on my ConcreteConverter CanConvert/ReadJson/WriteJson methods - they are never hit.

What am I missing, and how do i get the magic of:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);

to work properly.

I must note, I DO NOT want to just typnamehandling.objects (i think this is it) where it adds the ASSEMBLYNAME.TYPE etc into the json, thus bloating it. Just assume that the json above is what we have to work with, and the classes represented above, with their interfaces is what it must be deserialised to.

Regards,

Chud

PS - I have changed the class names/properties etc away from the actual real world implementation of what I am doing. So if they have come out with an error, I do apologise. But hopefully the majority is all ok, and the point/ultimate goal is understood :)

The_Chud
  • 991
  • 1
  • 11
  • 24

4 Answers4

9

The Problem

You cannot create instances of an interface, that is not allowed in .NET. You cannot do this:

var items = new IList<int>(); // Not allowed

Your error is clearly stating that:

Could not create an instance of type BLA. Type is an interface or abstract class and cannot be instantiated'

The Solution

Deserialize it to type List but assign it to type IList. Please note the types on the right side are not interfaces in code below except at the casting stage.

IList<IRootObject> MyDeserialisedObject = 
            JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString))
            .Cast<IRootObject>().ToList();

Some More Belabor

You may ask "Why do I need to cast" and why can I not do this instead?

IList<IRootObject> MyDeserialisedObject = 
            JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString));

That is not allowed in order to preserve reference identity.

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
3

You are trying to get JsonConvert to instantiate interfaces, which is not possible.

The problem is here:

IList<IRootObject> MyDeserializedObject = JsonConvert.Deserialize<IList<IRootObject>>(JsonString);
  • JsonConvert will process the json data you provide it.
  • Because you ask to convert it to an object of type IList<IRootObject>, JsonConvert will iteratively build a list of IRootObject objects.
  • For every element found in the list (inside the json data string), JsonConvert will create a new IRootObject() and populate its properties with the data it finds in the json data.

And that's the problem: new IRootObject() is not possible, because you cannot instantiate an interface. You can only instantiate classes.

Solution:

You already have a class RootObject. It already contains the same fields that your interface contains. However, RootObject is currently not implementing IRootObject.

Add the interface implementation to the class:

public class RootObject : IRootObject
{
    //make sure the implementation is correct
}

And then you can ask JsonConvert to give you a list of RootObject (not IRootObject!)

List<RootObject> MyDeserializedRootObjects = JsonConvert.Deserialize<List<RootObject>>(JsonString);

And if you want to turn the List<RootObject> into a IList<IRootObject>:

IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects.ToList<IRootObject>();

In case you are using .NET 3.5 or lower, you can cast the list as follows:

IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects .Cast<IRootObject>().ToList();

edit I may have done it a bit sneakily, but for the same reason that you cannot instantiate an IRootObject, you also cannot instantiate an IList. Both of these are interfaces.

The solution is the same for both: instantiate an object of a class that implements the interface (List and RootObject), and then cast them to their interfaces (IList and IRootObject).

Noah
  • 188
  • 4
  • 24
Flater
  • 12,908
  • 4
  • 39
  • 62
  • Hi Flater, fwiw - rootobject implements the irootobject interface. Just a typo. Have edited. Will read the rest of your post now, thanks. – The_Chud May 30 '17 at 12:20
  • Just for clarification - I could have easily selected your response as the answer. But in the interest of fairness, having CodingYoshi respond first with the casting, I selected his, although your answer is actually a little more comprehensive overall :) Being new to SO I hope I have done the right thing. – The_Chud Jun 07 '17 at 00:29
  • @The_Chud: It's a bit like Highlander over here. There can be only one :) – Flater Jun 07 '17 at 06:50
0

I believe you are never using your custom converter. According to the Newtonsoft.Json documentation (here), you should be using something like this:

IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString, new ConcreteListConverter());
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
0

Ignore the i++ that's just my iteration through my database columns. My reader here was reading a JSON object of type Student from the database and I used an IList like so and it worked. If it provides more clarity, IList is an interface and List is a concrete class or better yet List is a concrete type that implements the IList interface.


course.Student = reader.DeserializeObject<IList<Student>>(i++);

Shawn Fetanat
  • 57
  • 1
  • 8