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

c# - serializar - newtonsoft json serialize vb net



¿La deserialización Json.NET de Tuple<...> dentro de otro tipo no funciona? (2)

Usando Json.net, deserializar un tipo que contiene un Tuple<...> no funciona (la serialización funciona, pero la deserialización no):

[TestMethod] public void Test() { var orig = new TupleHolder("what????", true); var json = JsonConvert.SerializeObject(orig); Assert.AreEqual("{/"Tup/":{/"Item1/":/"what????/",/"Item2/":true}}", json); // great! serialization works like a charm! now let''s test deserialization: var dupl = JsonConvert.DeserializeObject<TupleHolder>(json); Assert.AreEqual("ZZZ", dupl.Tup.Item1); // pass! but it should be "what????"... what???? Assert.AreEqual(false, dupl.Tup.Item2); // pass! but it should be "true", right??? Assert.AreEqual(orig.Tup.Item1, dupl.Tup.Item1); // fail! Assert.AreEqual(orig.Tup.Item2, dupl.Tup.Item2); // fail! } public class TupleHolder { public Tuple<string, bool> Tup { get; set; } public TupleHolder() { Tup = new Tuple<string, bool>("ZZZ", false); } public TupleHolder(string s, bool b) { Tup = new Tuple<string, bool>(s, b); } }

Lo curioso es que la deserialización directa de Tuple<...> funciona:

[TestMethod] public void Test2() { var orig = new Tuple<string, bool>("ABC", true); var json = JsonConvert.SerializeObject(orig); var dupl = JsonConvert.DeserializeObject<Tuple<string, bool>>(json); Assert.AreEqual(orig, dupl); // direct deserialization of Tuple<...> works. }

¿Es un error de Json.NET o me falta algo aquí?


La solución, o la mía, de todos modos, es definir un convertidor personalizado para el Tuple.

Este ejemplo proporciona una solución concreta para un Tuple específico, pero puede genizarlo para hacer que la clase TupleConverter maneje cualquier combinación de tipos de valores. También podría hacerlo abstracto y tener tipos derivados implementar métodos de instanciación para cada elemento, para manejar tuplas con tipos de referencia.

public class TupleConverter : Newtonsoft.Json.JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Tuple<string, bool>) == objectType; } public override object ReadJson( Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { if (reader.TokenType == Newtonsoft.Json.JsonToken.Null) return null; var jObject = Newtonsoft.Json.Linq.JObject.Load(reader); var target = new Tuple<string, bool>( (string)jObject["Item1"], (bool)jObject["Item2"]); return target; } public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { serializer.Serialize(writer, value); } } public class TupleHolder { [Newtonsoft.Json.JsonConverter(typeof(TupleConverter))] public Tuple<string, bool> Tup { get; set; } public TupleHolder() { Tup = new Tuple<string, bool>("ZZZ", false); } public TupleHolder(string s, bool b) { Tup = new Tuple<string, bool>(s, b); } } [Test] public void Test() { var orig = new TupleHolder("what????", true); var json = Newtonsoft.Json.JsonConvert.SerializeObject(orig); Assert.AreEqual("{/"Tup/":{/"Item1/":/"what????/",/"Item2/":true}}", json); var dupl = Newtonsoft.Json.JsonConvert.DeserializeObject<TupleHolder>(json); // These succeed, now Assert.AreEqual(orig.Tup.Item1, dupl.Tup.Item1); Assert.AreEqual(orig.Tup.Item2, dupl.Tup.Item2); }


La respuesta proporcionada por Remi me ayudó. Tomé su TupleConverter y lo hice genérico para una 2-tupla. El concepto es el mismo para cualquier N-tupla.

Lo dejo aquí en caso de que ayude a alguien.

public class TupleConverter<U, V> : Newtonsoft.Json.JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Tuple<U, V>) == objectType; } public override object ReadJson( Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { if (reader.TokenType == Newtonsoft.Json.JsonToken.Null) return null; var jObject = Newtonsoft.Json.Linq.JObject.Load(reader); var target = new Tuple<U, V>( jObject["m_Item1"].ToObject<U>(), jObject["m_Item2"].ToObject<V>()); return target; } public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { serializer.Serialize(writer, value); } }

Nota: Mi Tuple fue serializada por JSON con m_Item1 y m_Item2 , así que tuve que cambiar jObject["ItemX"] a jObject["m_ItemX"]

Ejemplo de uso con una List<Tuple<int, User>> :

string result = "String to deserialize"; JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new TupleConverter<int, User>()); List<Tuple<int, User>> users = JsonConvert.DeserializeObject<List<Tuple<int, User>>>(result, settings);