Json.NET Tuple<...> 在另一种类型中的反序列化不起作用?
Json.NET deserialization of Tuple<...> inside another type doesn't work?
使用 Json.net,反序列化包含 Tuple<...>
的类型不起作用(序列化有效,但反序列化无效):
[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); }
}
有趣的是 Tuple<...>
的直接反序列化确实有效:
[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.
}
这是一个 Json.NET 错误还是我在这里遗漏了什么?
解决方案 - 或者我的解决方案 - 是为元组定义一个自定义转换器。
此示例为特定的 Tuple 提供了具体的解决方案,但您可以将其通用化以使 TupleConverter class 处理任何值类型的组合。也可以使它抽象并让派生类型为每个项目实现实例化方法,以处理具有引用类型的元组。
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);
}
Remi 提供的答案对我很有帮助。我采用了他的 TupleConverter
并将其设为 2 元组的泛型。这个概念对于任何 N 元组都是相同的。
我把它留在这里以防它对某人有帮助。
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);
}
}
Note: My Tuple was JSON serialized with m_Item1
and m_Item2
, so I had to change jObject["ItemX"]
to jObject["m_ItemX"]
使用示例 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);
我最后得到了一些更通用的东西,希望它能有所帮助
public class TupleConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var match = Regex.Match(objectType.Name, "Tuple`([0-9])", RegexOptions.IgnoreCase);
return match.Success;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
try
{
var tupleTypes = objectType.GetProperties().ToList().Select(p => p.PropertyType).ToArray();
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
var valueItems = new List<object>();
for (var i = 1; i <= tupleTypes.Length; i++)
valueItems.Add(jObject[$"m_Item{i}"].ToObject(tupleTypes[i - 1]));
var convertedObject = objectType.GetConstructor(tupleTypes)?.Invoke(valueItems.ToArray());
return convertedObject;
}
catch (Exception ex)
{
throw new Exception("Something went wrong in this implementation", ex);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
使用 Json.net,反序列化包含 Tuple<...>
的类型不起作用(序列化有效,但反序列化无效):
[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); }
}
有趣的是 Tuple<...>
的直接反序列化确实有效:
[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.
}
这是一个 Json.NET 错误还是我在这里遗漏了什么?
解决方案 - 或者我的解决方案 - 是为元组定义一个自定义转换器。
此示例为特定的 Tuple 提供了具体的解决方案,但您可以将其通用化以使 TupleConverter class 处理任何值类型的组合。也可以使它抽象并让派生类型为每个项目实现实例化方法,以处理具有引用类型的元组。
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);
}
Remi 提供的答案对我很有帮助。我采用了他的 TupleConverter
并将其设为 2 元组的泛型。这个概念对于任何 N 元组都是相同的。
我把它留在这里以防它对某人有帮助。
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);
}
}
Note: My Tuple was JSON serialized with
m_Item1
andm_Item2
, so I had to changejObject["ItemX"]
tojObject["m_ItemX"]
使用示例 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);
我最后得到了一些更通用的东西,希望它能有所帮助
public class TupleConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var match = Regex.Match(objectType.Name, "Tuple`([0-9])", RegexOptions.IgnoreCase);
return match.Success;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
try
{
var tupleTypes = objectType.GetProperties().ToList().Select(p => p.PropertyType).ToArray();
var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
var valueItems = new List<object>();
for (var i = 1; i <= tupleTypes.Length; i++)
valueItems.Add(jObject[$"m_Item{i}"].ToObject(tupleTypes[i - 1]));
var convertedObject = objectType.GetConstructor(tupleTypes)?.Invoke(valueItems.ToArray());
return convertedObject;
}
catch (Exception ex)
{
throw new Exception("Something went wrong in this implementation", ex);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}