2017-08-03 146 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);