4

使用Json.net,反序列化包含Tuple<...>不工作類型(串行化的作品,但反序列化不):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錯誤還是我在這裏丟失的東西?

+2

我覺得這是類似於你的問題... http://stackoverflow.com/questions/23017716/json-net-how-to-deserialize-without-using -default-constructor。這是由於使用了無參數的構造函數 –

+0

@JoelGregory,有些......事情是,如果我有一個'List '它將被正確地反序列化,只是'Tuple <...>'有問題。我想'Json.NET'檢查屬性/字段是否實現'IEnumerable' /'ICollection' /'IList'/...和'Tuple <...>'不...... – Tar

+0

@JoelGregory,解決方法可以使用'[JsonObject(MemberSerialization.Fields)]''但它會在'JSON'字符串中創建一個醜陋的' k__BackingField'字符串...我希望有這樣一個簡單的解決方案,沒有這個額外的字符串... – Tar

回答

1

無論如何,解決方案或我的解決方案都是爲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); 
    } 
+0

如果我記得正確地說,這就是我最終做的... – Tar

0

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

注:我的元組是JSON序列與m_Item1m_Item2,所以我不得不改變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);