2017-08-03 143 views
3

我收到了,看起來像這樣從在線服務提供商的數據:反序列化JSON陣列陣列成元組的名單使用Newtonsoft

{ 
    name: "test data", 
    data: [ 
    [ "2017-05-31", 2388.33 ], 
    [ "2017-04-30", 2358.84 ], 
    [ "2017-03-31", 2366.82 ], 
    [ "2017-02-28", 2329.91 ] 
    ], 
}  

我想它解析爲一個對象,它看起來像這樣:

public class TestData 
{ 
    public string Name; 
    public List<Tuple<DateTime, double>> Data; 
} 

我已經能夠找到的唯一的事情是如何解析的對象數組到tulples的列表,例如:Json.NET deserialization of Tuple<...> inside another type doesn't work?

有沒有寫一個自定義的合作方式能處理這個問題的逆變器?

+0

爲什麼元組?在大多數情況下,它們有點不好(除了C#7,但這真的是另一回事) – DavidG

+0

一個類也可以,但它是一種簡單的方法來定義包含兩個「不同」數據類型的東西......無論哪種方式會沒事的。 –

+0

@BrianRice:一般來說,可以編寫一個小類,它保存相同的數據,並且有更多有用的名稱。 Item1和Item2永遠不會爲可讀代碼做出...偶爾有時候你想要元組的功能(例如它的相等邏輯),但是更多的時候,一個小的命名類可以完美工作並且更具可讀性。 – Chris

回答

1

我把通用TupleConverter從這裏:Json.NET deserialization of Tuple<...> inside another type doesn't work? 並取得通用TupleListConverter。

用法:

public class TestData 
{ 
    public string Name; 
    [Newtonsoft.Json.JsonConverter(typeof(TupleListConverter<DateTime, double>))] 
    public List<Tuple<DateTime, double>> Data; 
} 

public void Test(string json) 
{ 
    var testData = JsonConvert.DeserializeObject<TestData>(json); 
    foreach (var tuple in testData.data) 
    { 
     var dateTime = tuple.Item1; 
     var price = tuple.Item2; 
     ... do something... 
    } 
} 

轉換器:

public class TupleListConverter<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 jArray = Newtonsoft.Json.Linq.JArray.Load(reader); 
     var target = new List<Tuple<U, V>>(); 

     foreach (var childJArray in jArray.Children<Newtonsoft.Json.Linq.JArray>()) 
     { 
      var tuple = new Tuple<U, V>(
       childJArray[0].ToObject<U>(), 
       childJArray[1].ToObject<V>() 
      ); 
      target.Add(tuple); 
     } 

     return target; 
    } 

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, value); 
    } 
} 
0

而不是使用元組,我會創建一個特定於任務的類。在這種情況下,您的JSON數據以字符串列表的形式出現,這對於處理有點尷尬。一種方法是反序列化爲List<List<string>>,然後再進行轉換。例如,我會去與3類是這樣的:

public class IntermediateTestData 
{ 
    public string Name; 
    public List<List<string>> Data; 
} 

public class TestData 
{ 
    public string Name; 
    public IEnumerable<TestDataItem> Data; 
} 

public class TestDataItem 
{ 
    public DateTime Date { get; set; } 
    public double Value { get; set; } 
} 

現在deserialise這樣的:

var intermediate = JsonConvert.DeserializeObject<IntermediateTestData>(json); 

var testData = new TestData 
{ 
    Name = intermediate.Name, 
    Data = intermediate.Data.Select(d => new TestDataItem 
    { 
     Date = DateTime.Parse(d[0]), 
     Value = double.Parse(d[1]) 
    }) 

}; 
1

因此,使用LINQ JSON.NET,我設法讓你規定它的工作...

var result = JsonConvert.DeserializeObject<JObject>(json); 
var data = new TestData 
{ 
    Name = (string)result["name"], 
    Data = result["data"] 
     .Select(t => new Tuple<DateTime, double>(DateTime.Parse((string)t[0]), (double)t[1])) 
     .ToList() 
}; 

這是我寫的全部測試

public class TestData 
{ 
    public string Name; 
    public List<Tuple<DateTime, double>> Data; 
} 

[TestMethod] 
public void TestMethod1() 
{ 
    var json = 
    @"{ 
     name: ""test data"", 
     data: [ 
     [ ""2017-05-31"", 2388.33 ], 
     [ ""2017-04-30"", 2358.84 ], 
     [ ""2017-03-31"", 2366.82 ], 
     [ ""2017-02-28"", 2329.91 ] 
     ], 
    }"; 

    var result = JsonConvert.DeserializeObject<JObject>(json); 
    var data = new TestData 
    { 
     Name = (string)result["name"], 
     Data = result["data"] 
      .Select(t => new Tuple<DateTime, double>(DateTime.Parse((string)t[0]), (double)t[1])) 
      .ToList() 
    }; 

    Assert.AreEqual(2388.33, data.Data[0].Item2); 
} 

但是,雖然這可能有效,但我同意其他評論/答案,使用元組可能不是正確的方法。僅僅因爲Tuple<,>Item1Item2屬性,使用混凝土POCO的長期運行肯定會變得更加可維護。

他們都不是最描述...

+0

我同意......整個問題的關鍵是將一些數組轉換爲(某種)對象的列表......無論是元組還是類......您的技術都可以以任何方式工作。 –

0

如果有人有興趣在ValueTuples一個更通用的解決方案

public class TupleConverter : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var type = value.GetType(); 
     var array = new List<object>(); 
     FieldInfo fieldInfo; 
     var i = 1; 

     while ((fieldInfo = type.GetField($"Item{i++}")) != null) 
      array.Add(fieldInfo.GetValue(value)); 

     serializer.Serialize(writer, array); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var argTypes = objectType.GetGenericArguments(); 
     var array = serializer.Deserialize<JArray>(reader); 
     var items = array.Select((a, index) => a.ToObject(argTypes[index])).ToArray(); 

     var constructor = objectType.GetConstructor(argTypes); 
     return constructor.Invoke(items); 
    } 

    public override bool CanConvert(Type type) 
    { 
     return type.Name.StartsWith("ValueTuple`"); 
    } 
} 

用法如下:

var settings = new JsonSerializerSettings(); 
settings.Converters.Add(new TupleConverter()); 

var list = new List<(DateTime, double)> 
{ 
    (DateTime.Now, 7.5) 
}; 
var json = JsonConvert.SerializeObject(list, settings); 
var result = JsonConvert.DeserializeObject(json, list.GetType(), settings);