使用Newtonsoft将JSON数组数组反序列化为List of Tuples

我从在线服务提供商处收到的数据如下:

{ 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> Data; } 

我唯一能找到的是如何将一组对象解析成一个tulples列表,例如: Json.NET反序列化其他类型中的Tuple 不起作用?

有没有办法编写一个可以处理这个问题的自定义转换器?

所以使用JSON.NET LINQ,我设法按照你的规定让它工作……

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

这是我写的完整测试

 public class TestData { public string Name; public List> 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(json); var data = new TestData { Name = (string)result["name"], Data = result["data"] .Select(t => new Tuple(DateTime.Parse((string)t[0]), (double)t[1])) .ToList() }; Assert.AreEqual(2388.33, data.Data[0].Item2); } 

然而,虽然这可能有用,但我同意其余的评论/答案,使用元组可能不是正确的方法。 从长远来看,使用具体的POCO肯定会更加难以维护,因为Tuple<,>Item1Item2属性。

它们不是最具描述性的……

我从这里采用了通用的TupleConverter: 在另一种类型中的Tuple <...>的Json.NET反序列化不起作用? 并制作了一个通用的TupleListConverter。

用法:

 public class TestData { public string Name; [Newtonsoft.Json.JsonConverter(typeof(TupleListConverter))] public List> Data; } public void Test(string json) { var testData = JsonConvert.DeserializeObject(json); foreach (var tuple in testData.data) { var dateTime = tuple.Item1; var price = tuple.Item2; ... do something... } } 

转换器:

 public class TupleListConverter : Newtonsoft.Json.JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Tuple) == 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>(); foreach (var childJArray in jArray.Children()) { var tuple = new Tuple( childJArray[0].ToObject(), childJArray[1].ToObject() ); target.Add(tuple); } return target; } public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { serializer.Serialize(writer, value); } } 

如果有人对ValueTuples的更通用的解决方案感兴趣

 public class TupleConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var type = value.GetType(); var array = new List(); 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(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); 

我会创建一个特定于任务的类,而不是使用元组。 在这种情况下,您的JSON数据作为字符串列表的列表进入,这有点难以处理。 一种方法是反序列化为List> ,然后转换。 例如,我会使用这样的3个类:

 public class IntermediateTestData { public string Name; public List> Data; } public class TestData { public string Name; public IEnumerable Data; } public class TestDataItem { public DateTime Date { get; set; } public double Value { get; set; } } 

现在反序列化如下:

 var intermediate = JsonConvert.DeserializeObject(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]) }) };