[英]Deserialize JSON array of arrays into List of Tuples using 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反序列化其他類型中的Tuple <...>不起作用?
有沒有辦法編寫一個可以處理這個問題的自定義轉換器?
如果有人對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);
我會創建一個特定於任務的類,而不是使用元組。 在這種情況下,您的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; }
}
現在反序列化如下:
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])
})
};
我從這里采用了通用的TupleConverter: Json.NET反序列化了另一種類型中的Tuple <...>不起作用? 並制作了一個通用的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);
}
}
所以使用JSON.NET LINQ,我設法按照你的規定讓它工作......
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);
}
然而,雖然這可能有效,但我同意其余的評論/答案,使用元組可能不是正確的方法。 從長遠來看,使用具體的POCO肯定是一個更難維護的地獄,因為Tuple<,>
的Item1
和Item2
屬性。
它們不是最具描述性的......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.