Json.NET 元组的反序列化<..>在另一种类型中不起作用

本文关键字:另一种 类型 不起作用 NET 元组 反序列化 Json | 更新日期: 2023-09-27 18:37:21

使用 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 错误还是我在这里错过了什么?

Json.NET 元组的反序列化<..>在另一种类型中不起作用

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

注意:我的元组是用 m_Item1m_Item2 序列化的 JSON 的,所以我不得不jObject["ItemX"]更改为 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);
解决方案 -

或者我的,无论如何 - 是为元组定义一个自定义转换器。

此示例为特定元组提供了具体的解决方案,但您可以将其泛型化,以使 TupleConverter 类处理值类型的任意组合。还可以使其抽象,并让派生类型为每个项目实现实例化方法,以处理具有引用类型的元组。

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

我最终得到了更通用的东西,希望它有所帮助

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