【问题标题】:Json.NET deserialization of Tuple<...> inside another type doesn't work?Tuple<...> 在另一种类型中的 Json.NET 反序列化不起作用?
【发布时间】:2015-01-12 09:32:45
【问题描述】:

使用 Json.net,反序列化包含 Tuple&lt;...&gt; 的类型不起作用(序列化有效,但反序列化无效):

[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&lt;...&gt; 的直接反序列化确实有效:

[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 错误还是我在这里遗漏了什么?

【问题讨论】:

  • 我认为这与您的问题相似...stackoverflow.com/questions/23017716/…。这是由于使用了无参数构造函数
  • @JoelGregory,有点……问题是,如果我有一个List&lt;T&gt;,它将被正确反序列化,只是Tuple&lt;...&gt; 是有问题的。我想Json.NET 检查属性/字段是否实现IEnumerable/ICollection/IList/... 而Tuple&lt;...&gt; 没有...
  • @JoelGregory,一种解决方法可能是使用[JsonObject(MemberSerialization.Fields)],但它会在JSON 字符串中创建一个丑陋的&lt;Tup&gt;k__BackingField 字符串......我希望有一个这样的简单解决方案,没有这个额外的字符串...

标签: c# serialization json.net deserialization json-deserialization


【解决方案1】:

雷米提供的答案帮助了我。我拿了他的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&lt;Tuple&lt;int, User&gt;&gt; 的使用示例:

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

【讨论】:

    【解决方案2】:

    解决方案——或者我的,无论如何——是为元组定义一个自定义转换器。

    此示例为特定的 Tuple 提供了具体的解决方案,但您可以对其进行泛化以使 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);
        }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多