Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize collection of interface-instances?

Tags:

c#

json.net

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?

like image 741
user1130329 Avatar asked Apr 08 '13 13:04

user1130329


People also ask

Can you deserialize Interface C#?

Short answer: no. Deserialization involves creating an instance, and you can't create instances of interfaces.

What is JsonSerializer deserialize?

Overloads. Deserialize(Stream, Type, JsonSerializerOptions) Reads the UTF-8 encoded text representing a single JSON value into a returnType . The Stream will be read to completion.

What is JsonSerializerSettings?

Specifies the settings on a JsonSerializer object. Newtonsoft.Json. JsonSerializerSettings. Namespace: Newtonsoft.Json.

How does Newtonsoft JSON deserialize work?

Newtonsoft. Json uses reflection to get constructor parameters and then tries to find closest match by name of these constructor parameters to object's properties. It also checks type of property and parameters to match. If there is no match found, then default value will be passed to this parameterized constructor.


2 Answers

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

like image 193
Ben Jenkinson Avatar answered Oct 12 '22 06:10

Ben Jenkinson


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();     } } 
like image 21
Piotr Stapp Avatar answered Oct 12 '22 05:10

Piotr Stapp