serializeobject serialize newtonsoft jsonconvert example deserialize c# json serialization json.net deserialization

c# - jsonconvert - NewtonSoft.Json Serialize y Deserialize clase con propiedad de tipo IEnumerable<ISomeInterface>



newtonsoft json example serialize (7)

En mis proyectos, este fragmento de código siempre funcionó como un serializador predeterminado que serializa el valor especificado como si no hubiera un convertidor especial:

serializer.Serialize(writer, value);

Estoy intentando mover un poco de código para consumir los datos Json generados por la API web ASP.NET MVC en lugar de SOAP Xml.

Me he encontrado con un problema con las propiedades de serialización y deserialización de tipo:

IEnumerable<ISomeInterface>.

Aquí hay un ejemplo simple:

public interface ISample{ int SampleId { get; set; } } public class Sample : ISample{ public int SampleId { get; set; } } public class SampleGroup{ public int GroupId { get; set; } public IEnumerable<ISample> Samples { get; set; } } }

Puedo serializar instancias de SampleGroup fácilmente con:

var sz = JsonConvert.SerializeObject( sampleGroupInstance );

Sin embargo, la deserialización correspondiente falla:

JsonConvert.DeserializeObject<SampleGroup>( sz );

con este mensaje de excepción:

"No se pudo crear una instancia de tipo JsonSerializationExample.ISample. Type es una interfaz o clase abstracta y no se puede instalar".

Si obtengo un JsonConverter, puedo decorar mi propiedad de la siguiente manera:

[JsonConverter( typeof (SamplesJsonConverter) )] public IEnumerable<ISample> Samples { get; set; }

Aquí está el JsonConverter:

public class SamplesJsonConverter : JsonConverter{ public override bool CanConvert( Type objectType ){ return ( objectType == typeof (IEnumerable<ISample>) ); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ){ var jA = JArray.Load( reader ); return jA.Select( jl => serializer.Deserialize<Sample>( new JTokenReader( jl ) ) ).Cast<ISample>( ).ToList( ); } public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ){ ... What works here? } }

Este convertidor resuelve el problema de deserialización, pero no sé cómo codificar el método WriteJson para que la serialización vuelva a funcionar.

¿Alguien puede ayudar?

¿Es esta una manera "correcta" de resolver el problema en primer lugar?


Es bastante simple y el soporte provisto por json.net, solo tiene que usar los siguientes JsonSettings mientras serializa y deserializa:

JsonConvert.SerializeObject(graph,Formatting.None, new JsonSerializerSettings() { TypeNameHandling =TypeNameHandling.Objects, TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple });

y para la deserialización use el siguiente código:

JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bData),type, new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Objects} );

Simplemente tome nota del inicializador de objetos JsonSerializerSettings, que es importante para usted.


Gran solución, ¡gracias! Tomé la pregunta de AndyDBell y la respuesta de Cuong Le para construir un ejemplo con la implementación de dos interfaces diferentes:

public interface ISample { int SampleId { get; set; } } public class Sample1 : ISample { public int SampleId { get; set; } public Sample1() { } } public class Sample2 : ISample { public int SampleId { get; set; } public String SampleName { get; set; } public Sample2() { } } public class SampleGroup { public int GroupId { get; set; } public IEnumerable<ISample> Samples { get; set; } } class Program { static void Main(string[] args) { //Sample1 instance var sz = "{/"GroupId/":1,/"Samples/":[{/"SampleId/":1},{/"SampleId/":2}]}"; var j = JsonConvert.DeserializeObject<SampleGroup>(sz, new SampleConverter<Sample1>()); foreach (var item in j.Samples) { Console.WriteLine("id:{0}", item.SampleId); } //Sample2 instance var sz2 = "{/"GroupId/":1,/"Samples/":[{/"SampleId/":1, /"SampleName/":/"Test1/"},{/"SampleId/":2, /"SampleName/":/"Test2/"}]}"; var j2 = JsonConvert.DeserializeObject<SampleGroup>(sz2, new SampleConverter<Sample2>()); //Print to show that the unboxing to Sample2 preserved the SampleName''s values foreach (var item in j2.Samples) { Console.WriteLine("id:{0} name:{1}", item.SampleId, (item as Sample2).SampleName); } Console.ReadKey(); } }

Y una versión genérica para SampleConverter:

public class SampleConverter<T> : CustomCreationConverter<ISample> where T: new () { public override ISample Create(Type objectType) { return ((ISample)new T()); } }


No necesita usar JsonConverterAttribute , mantener su modelo limpio, también usar CustomCreationConverter , el código es más simple:

public class SampleConverter : CustomCreationConverter<ISample> { public override ISample Create(Type objectType) { return new Sample(); } }

Entonces:

var sz = JsonConvert.SerializeObject( sampleGroupInstance ); JsonConvert.DeserializeObject<SampleGroup>( sz, new SampleConverter());

Documentación: Deserializar con CustomCreationConverter


Tengo esto para trabajar:

conversión explícita

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonObj = serializer.Deserialize<List<SomeObject>>(reader); var conversion = jsonObj.ConvertAll((x) => x as ISomeObject); return conversion; }


Teniendo eso:

public interface ITerm { string Name { get; } } public class Value : ITerm... public class Variable : ITerm... public class Query { public IList<ITerm> Terms { get; } ... }

Logré el truco de conversión implementando eso:

public class TermConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var field = value.GetType().Name; writer.WriteStartObject(); writer.WritePropertyName(field); writer.WriteValue((value as ITerm)?.Name); writer.WriteEndObject(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var properties = jsonObject.Properties().ToList(); var value = (string) properties[0].Value; return properties[0].Name.Equals("Value") ? (ITerm) new Value(value) : new Variable(value); } public override bool CanConvert(Type objectType) { return typeof (ITerm) == objectType || typeof (Value) == objectType || typeof (Variable) == objectType; } }

Me permite serializar y deserializar en JSON como:

string JsonQuery = "{/"Terms/":[{/"Value/":/"This is /"},{/"Variable/":/"X/"},{/"Value/":/"!/"}]}"; ... var query = new Query(new Value("This is "), new Variable("X"), new Value("!")); var serializeObject = JsonConvert.SerializeObject(query, new TermConverter()); Assert.AreEqual(JsonQuery, serializeObject); ... var queryDeserialized = JsonConvert.DeserializeObject<Query>(JsonQuery, new TermConverter());


Resolví ese problema usando una configuración especial para JsonSerializerSettings que se llama TypeNameHandling.All

La configuración de TypeNameHandling incluye información de tipo al serializar JSON y leer información de tipo para que los tipos de creación se creen al deserializar JSON

Publicación por entregas:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; var text = JsonConvert.SerializeObject(configuration, settings);

Deserialización:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; var configuration = JsonConvert.DeserializeObject<YourClass>(json, settings);

La clase YourClass puede tener cualquier tipo de campos de tipo base y se serializará correctamente.