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

时间:2017-08-03 15:36:56

标签: c# arrays json tuples deserialization

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

{
  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?

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

3 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:1)

因此,使用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);
}

然而,虽然这可能有效,但我同意其余的评论/答案,使用元组可能不是正确的方法。由于Item1的{​​{1}}和Item2属性,使用具体的POCO绝对会在长期内变得更加难以维护。

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

答案 2 :(得分: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; }
}

现在像这样反序列化:

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

};