76
votes

I would like to serialize this code via json.net:

public interface ITestInterface
{
    string Guid {get;set;}
}

public class TestClassThatImplementsTestInterface1
{
    public string Guid { get;set; }
}

public class TestClassThatImplementsTestInterface2
{
    public string Guid { get;set; }
}


public class ClassToSerializeViaJson
{
    public ClassToSerializeViaJson()
    {             
         this.CollectionToSerialize = new List<ITestInterface>();
         this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
         this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
    }
    List<ITestInterface> CollectionToSerialize { get;set; }
}

I want to serialize/deserialize ClassToSerializeViaJson with json.net. Serialization is working, but deserialization gives me this error:

Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.

So how can I deserialize the List<ITestInterface> collection?

9
What have you tried already? Have you even read the documentation for JSON.NET?! I'm pretty sure serialisation and deserialisation is one of the first things such documentation is going to cover.Clint
Yes Serialization is working but I get an Error when I try to deserialize: Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.user1130329
then perhaps you ought to lead with that in your question? Rather than asking such open-ended "how can I?" "It doesn't work" questions, you really need to provide all the information, what error is it? Where is it happening? What have you tried to fix it so far? Please edit your question with those things so the community can better help you instead of flagging your questions.Clint
Nicholas Westby provided a great solution in a awesome articleA. Morel

9 Answers

91
votes

I found this question while trying to do this myself. After I implemented Piotr Stapp's(Garath's) answer, I was struck by how simple it seemed. If I was merely implementing a method that was already being passed the exact Type (as a string) that I wanted to instantiate, why wasn't the library binding it automatically?

I actually found that I didn't need any custom binders, Json.Net was able to do exactly what I needed, provided I told it that was what I was doing.

When serializing:

string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});

When de-serializing:

var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects
});

Relevant documentation: Serialization Settings for Json.NET and TypeNameHandling setting

43
votes

Bellow full working example with what you want to do:

public interface ITestInterface
{
    string Guid { get; set; }
}

public class TestClassThatImplementsTestInterface1 : ITestInterface
{
    public string Guid { get; set; }
    public string Something1 { get; set; }
}

public class TestClassThatImplementsTestInterface2 : ITestInterface
{
    public string Guid { get; set; }
    public string Something2 { get; set; }
}

public class ClassToSerializeViaJson
{
    public ClassToSerializeViaJson()
    {
        this.CollectionToSerialize = new List<ITestInterface>();
    }
    public List<ITestInterface> CollectionToSerialize { get; set; }
}

public class TypeNameSerializationBinder : SerializationBinder
{
    public string TypeFormat { get; private set; }

    public TypeNameSerializationBinder(string typeFormat)
    {
        TypeFormat = typeFormat;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = null;
        typeName = serializedType.Name;
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        var resolvedTypeName = string.Format(TypeFormat, typeName);
        return Type.GetType(resolvedTypeName, true);
    }
}

class Program
{
    static void Main()
    {
        var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
        var toserialize = new ClassToSerializeViaJson();

        toserialize.CollectionToSerialize.Add(
            new TestClassThatImplementsTestInterface1()
            {
                Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
            });
        toserialize.CollectionToSerialize.Add(
            new TestClassThatImplementsTestInterface2()
            {
                Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
            });

        string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                Binder = binder
            });
        var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                Binder = binder 
            });

        Console.ReadLine();
    }
}
27
votes

I was also surprised by the simplicity in Garath's, and also came to the conclusion that the Json library can do it automatically. But I also figured that it's even simpler than Ben Jenkinson's answer (even though I can see it has been modified by the developer of the json library himself). From my testings, all you need to do is set TypeNameHandling to Auto, like this:

var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
       new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString, 
       new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });

From TypeNameHandling Enumeration documentation

Auto: Include the .NET type name when the type of the object being serialized is not the same as its declared type. Note that this doesn't include the root serialized object by default.

7
votes

Using the default settings, you cannot. JSON.NET has no way of knowing how to deserialize an array. However, you can specify which type converter to use for your interface type. To see how to do this, see this page: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/

You can also find information about this problem at this SO question: Casting interfaces for deserialization in JSON.NET

7
votes

This is an old question, but thought I'd add a more in-depth answer (in the form of an article I wrote): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/

TLDR: Rather than configure Json.NET to embed type names in the serialized JSON, you can use a JSON converter to figure out which class to deserialize to using whatever custom logic you like.

This has the advantage that you can refactor your types without worrying about deserialization breaking.

5
votes

It can be done with JSON.NET and JsonSubTypes attributes:

[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
    string Guid { get; set; }
}

public class Test1 : ITestInterface
{
    public string Guid { get; set; }
    public string Something1 { get; set; }
}

public class Test2 : ITestInterface
{
    public string Guid { get; set; }
    public string Something2 { get; set; }
}

and simply:

var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
4
votes

I wanted to deserialize JSON that wasn't serialized by my application, hence I needed to specify the concrete implementation manually. I have expanded on Nicholas's answer.

Lets say we have

public class Person
{
    public ILocation Location { get;set; }
}

and the concrete instance of

public class Location: ILocation
{
    public string Address1 { get; set; }
    // etc
}

Add in this class

public class ConfigConverter<I, T> : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(I);
    }
    public override void WriteJson(JsonWriter writer,
        object value, JsonSerializer serializer)
    {
        throw new InvalidOperationException("Use default serialization.");
    }

    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var deserialized = (T)Activator.CreateInstance(typeof(T));
        serializer.Populate(jsonObject.CreateReader(), deserialized);
        return deserialized;
    }
}

Then define your interfaces with the JsonConverter attribute

public class Person
{
    [JsonConverter(typeof(ConfigConverter<ILocation, Location>))]
    public ILocation Location { get;set; }
}
3
votes

Near-duplicate of Inrego's answer, but it's worthy of further explanation:

If you use TypeNameHandling.Auto then it only includes the type/assembly name when it needs to (i.e. interfaces and base/derived classes). So your JSON is cleaner, smaller, more specific.

Which isn't that one of the main selling points of it over XML/SOAP?

2
votes

Avoid TypeNameHandling.Auto when possible, particularly with user-controllable values.

You will need to write your own deserializer for the collection type.

Rather than repeat others who have already posted boilerplate converter code (particularly Nicholas Westby, whose blog post was quite useful and is linked above), I have included the relevant changes for deserializing a collection of interfaces (I had an enum interface property to distinguish implementors):

    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        Collection<T> result = new Collection<T>();
        var array = JArray.Load(reader);
        foreach (JObject jsonObject in array)
        { 
            var rule = default(T);
            var value = jsonObject.Value<string>("MyDistinguisher");
            MyEnum distinguisher;
            Enum.TryParse(value, out distinguisher);
            switch (distinguisher)
            {
                case MyEnum.Value1:
                    rule = serializer.Deserialize<Type1>(jsonObject.CreateReader());
                    break;
                case MyEnum.Value2:
                    rule = serializer.Deserialize<Type2>(jsonObject.CreateReader());
                    break;
                default:
                    rule = serializer.Deserialize<Type3>(jsonObject.CreateReader());
                    break;
            }
            result.Add(rule);
        }
        return result;
    }

I hope this is helpful to the next person looking for an interface collection deserializer.