JSON.NET: ¿cómo deserializar la colección de instancias de interfaz?

Me gustaría serializar este código a través de 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(); this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() ); this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() ); } List CollectionToSerialize { get;set; } } 

Quiero serializar / deserializar ClassToSerializeViaJson con json.net. La serialización funciona, pero la deserialización me da este error:

Newtonsoft.Json.JsonSerializationException: no se pudo crear una instancia de tipo ITestInterface. Tipo es una interfaz o clase abstracta y no se puede crear una instancia.

Entonces, ¿cómo puedo deserializar la colección List ?

Buen ejemplo de trabajo completo con lo que quieres hacer:

 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(); } public List 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(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = binder }); Console.ReadLine(); } } 

Encontré esta pregunta al intentar hacer esto yo mismo. Después de implementar la respuesta de Garath , me sorprendió lo simple que parecía. Si simplemente estaba implementando un método que ya estaba siendo pasado, el Tipo exacto (como una cadena) que quería crear una instancia, ¿por qué la biblioteca no lo vinculaba automáticamente?

De hecho, descubrí que no necesitaba ningún archivador personalizado, Json.Net podía hacer exactamente lo que necesitaba, siempre que le dijera que eso era lo que estaba haciendo.

Al serializar:

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

Al deserializar:

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

Documentación relevante: Configuraciones de serialización para la configuración de Json.NET y TypeNameHandling

También me sorprendió la simplicidad en Garath’s, y también llegué a la conclusión de que la biblioteca Json puede hacerlo automáticamente. Pero también pensé que es incluso más simple que la respuesta de Ben Jenkinson (aunque puedo ver que ha sido modificado por el propio desarrollador de la biblioteca json). De mis pruebas, todo lo que necesita hacer es establecer TypeNameHandling en Auto, así:

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

Usando la configuración predeterminada, no puedes. JSON.NET no tiene forma de saber cómo deserializar una matriz. Sin embargo, puede especificar qué tipo de convertidor usar para su tipo de interfaz. Para ver cómo hacerlo, consulte esta página: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/

También puede encontrar información sobre este problema en esta pregunta SO: Interfaces de conversión para deserialización en JSON.NET

Esta es una vieja pregunta, pero pensé que agregaría una respuesta más profunda (en la forma de un artículo que escribí): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet /

TLDR: en lugar de configurar Json.NET para incrustar nombres de tipos en el JSON serializado, puede usar un convertidor JSON para averiguar a qué clase deserializar utilizando la lógica personalizada que desee.

Esto tiene la ventaja de que puede refactorizar sus tipos sin preocuparse por la deserialización.

Casi duplicado de la respuesta de Inrego, pero es digno de una mayor explicación:

Si usa TypeNameHandling.Auto entonces solo incluye el nombre del tipo / ensamblaje cuando lo necesite (es decir, interfaces y clases base / derivadas). Entonces su JSON es más limpio, más pequeño, más específico.

¿Cuál no es ese uno de los principales puntos de venta de XML / SOAP?

Quería deserializar JSON que mi aplicación no serializaba, por lo tanto, necesitaba especificar la implementación concreta manualmente. Me he expandido en la respuesta de Nicholas.

Digamos que tenemos

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

y la instancia concreta de

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

Añadir en esta clase

 public class ConfigConverter : 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; } } 

Luego defina sus interfaces con el atributo JsonConverter

 public class Person { [JsonConverter(typeof(ConfigConverter))] public ILocation Location { get;set; } } 

Se puede hacer con los atributos JSON.NET y JsonSubTypes :

 [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; } } 

y simplemente:

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