76

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?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
zpete
  • 1,725
  • 2
  • 18
  • 31
  • 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 Apr 08 '13 at 13:39
  • 1
    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. – zpete Apr 08 '13 at 13:47
  • 2
    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 Apr 08 '13 at 13:48
  • 1
    Nicholas Westby provided a great solution in a [awesome article](http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/) – A. Morel Feb 24 '18 at 12:02

9 Answers9

91

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

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
Ben Jenkinson
  • 1,806
  • 1
  • 16
  • 31
  • 17
    This does work, but people should understand this can bloat the size of your json big time. For me, it increased output file size by over 10X. –  Feb 16 '15 at 04:06
  • 12
    Note that putting concrete type names in the resulting JSON can be considered leaking implementation details and is definitely not clean if the JSON is used anywhere outside your own code. Also, if the JSON you're deserializing comes from external source, expecting it to include your type names is unreasonable. – Jacek Gorgoń Aug 29 '15 at 22:09
  • 4
    With this solution, one should sanitize the incoming types to avoid potential security hazards. See [TypeNameHandling caution in Newtonsoft Json](https://stackoverflow.com/q/39565954/3744182) for details. – dbc Feb 02 '18 at 21:26
  • [Ingero's answer](https://stackoverflow.com/questions/15880574/json-net-how-to-deserialize-collection-of-interface-instances/30407666#30407666) below minimizes pollution of $types by using TypeNameHandling.Auto instead of TypeNameHandling.Objects – Michael Freidgeim Jan 23 '19 at 11:57
  • I agree with @JacekGorgoń; I'm getting my JSON automatically from an ASP.NET core controller. – rory.ap Apr 25 '19 at 12:43
  • For deserialization I get the same exception as the question poster with both `TypeNameHandling.Objects` and `TypeNameHandling.Auto` - I'm using Newstonsoft.Json V12.3. – Matt Arnold Feb 25 '20 at 17:02
43

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();
    }
}
Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
Piotr Stapp
  • 19,392
  • 11
  • 68
  • 116
  • 1
    You can use inside the binder the FullyQualifiedName instead of name and you won't have to pass the TypeFormat – Slava Shpitalny Mar 09 '17 at 16:37
  • 4
    This worked well for me. A few updates since this was answered a while ago. It appears they are now using an interface `ISerializationBinder` rather than overriding `SerializationBinder`. Also in `BindToName` I used `serializedType.AssemblyQualifiedName` instead of Name and that passes both the `assemblyName` and fully qualified `typeName` to `BindToType` so now there is no need for a constructor. Then update `BindToType` with `var resolvedTypeName = string.Format("{0}, {1}", typeName,assemblyName); and everything should work without providing the namespace & assembly in the constructor` – Crob Mar 14 '18 at 14:40
  • Thanks for this, really saved the day! – Ben D May 21 '21 at 10:55
28

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.

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
Inrego
  • 1,524
  • 1
  • 15
  • 25
  • 2
    It is a few years after the original post, but this is an up to date and working way of doing it. The price you pay is that the types names are output as "$type" attributes on each object in JSON, but that is fine in many circumstances. – Grubl3r Feb 10 '16 at 12:41
  • 1
    This is the correct solution, also for more complicated data structures. Took me literally days to find it though... You can add to the settings `TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple` to shorten the type output as much as possible – code_gamer Dec 12 '17 at 10:12
  • 1
    With this solution also, one should sanitize the incoming types to avoid potential security hazards. See [TypeNameHandling caution in Newtonsoft Json](https://stackoverflow.com/q/39565954/3744182) for details. – dbc Feb 02 '18 at 21:26
  • It should be noted that this only works in the case where you have control over the json when it is created. If it is created from anywhere else this deserialization will not work. – Little geek Aug 12 '19 at 10:48
8

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.

Nicholas Westby
  • 1,109
  • 13
  • 32
7

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

Community
  • 1
  • 1
Erik Schierboom
  • 16,301
  • 10
  • 64
  • 81
5

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);
manuc66
  • 2,701
  • 29
  • 28
4

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; }
}
Adam
  • 16,089
  • 6
  • 66
  • 109
  • Some implementations use `serializer.Populate` others use `jsonObject.ToObject`. Is there a difference? – xr280xr Jul 28 '20 at 02:32
3

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?

sliderhouserules
  • 3,415
  • 24
  • 32
2

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.