Преобразование массива candles из ответа в Список исторических объектов. Дополнительные детали в теле

#c# #arrays #json #facebook-c#-sdk

#c# #массивы #json #facebook-c #-sdk

Вопрос:

У меня есть следующий объект, получающий возврат от одного из API.

 {{
  "status": "success",
  "data": {
    "candles": [
      [
        "2020-11-01T18:30:00 00:00",
        1065,
        1079.95,
        1051.1,
        1072.3,
        7183119
      ],
      [
        "2020-11-02T18:30:00 00:00",
        1072,
        1079.4,
        1057.5,
        1062.55,
        7204782
      ],]
  }
}}
 

Я хочу преобразовать данные о свечах в List<Historical>
Вот что я пробовал

 foreach (ArrayList item in historicalData["data"]["candles"])
                historicals.Add(new Historical(item));
 

Но это дает мне следующую ошибку:

 Cannot convert type 'Newtonsoft.Json.Linq.JArray' to 'System.Collections.ArrayList
 

Я пробовал использовать JsonConvert .DeserializeObject для прямого преобразования строки в объект с помощью следующего кода:

 foreach (var item in historicalData["data"]["candles"]) {
                var historical = JsonConvert.DeserializeObject<Historical>(item.ToString());
 

Но получил эту ошибку:

 Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'QuantConnect.Brokerages.Zerodha.Messages.Historical' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path '', line 1, position 1.
 

Историческое определение:

 public struct Historical
    {
        public Historical(dynamic data)
        {
            TimeStamp = Convert.ToDateTime(data[0], CultureInfo.InvariantCulture);
            Open = Convert.ToDecimal(data[1], CultureInfo.InvariantCulture);
            High = Convert.ToDecimal(data[2], CultureInfo.InvariantCulture);
            Low = Convert.ToDecimal(data[3], CultureInfo.InvariantCulture);
            Close = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture);
            Volume = Convert.ToUInt32(data[5], CultureInfo.InvariantCulture);
            OI = data.Count > 6 ? Convert.ToUInt32(data[6], CultureInfo.InvariantCulture) : 0;
        }

        public DateTime TimeStamp { get; }
        public decimal Open { get; }
        public decimal High { get; }
        public decimal Low { get; }
        public decimal Close { get; }
        public UInt32 Volume { get; }
        public UInt32 OI { get; }
    }
 

Есть ли правильный / элегантный способ заставить его работать. Также, что я здесь чего-то не хватает?

Комментарии:

1. Пробовал это, но получаю сообщение об ошибке « Не удается десериализовать текущий JSON …«

2. Да, я не заметил, что какой-то веб-разработчик решил разбить кучу типизированных данных на список и назвать его candle .

Ответ №1:

  1. Основная проблема заключается в том, что это недопустимый json. Однако давайте исправим это и предположим, что это правильно. Если это то, что вы получаете от некоторых ресурсов, я предлагаю вам связаться с ними и пожаловаться.
  2. Существует множество способов сделать это. Поскольку вы использовали JTokens, я дам вам аналогичный ответ.

Дано

 {
   "status":"success",
   "data":{
      "candles":[
         [
            "2020-11-01T18:30:00 00:00",
            1065,
            1079.95,
            1051.1,
            1072.3,
            7183119
         ],
         [
            "2020-11-02T18:30:00 00:00",
            1072,
            1079.4,
            1057.5,
            1062.55,
            7204782
         ]
      ]
   }
}
 

И

 public class Historical
{
   public DateTime TimeStamp { get; set; }
   public decimal Open { get; set; }
   public decimal High { get; set; }
   public decimal Low { get; set; }
   public decimal Close { get; set; }
   public UInt32 Volume { get; set; }
   public UInt32 OI { get; set; }
}
 

Использование

 var jtoken = JToken.Parse(input);

var data = jtoken["data"]["candles"]
    .Select(x => new Historical
    {
       TimeStamp = x[0].Value<DateTime>(),
       Open = x[1].Value<decimal>(),
       Low = x[2].Value<decimal>(),
       High = x[3].Value<decimal>(),
       Close = x[4].Value<decimal>()
       //...
    });

foreach (var item in data)
   Console.WriteLine($"{item.TimeStamp},Open {item.Open},Low {item.Low} ...");
 

Выходной сигнал

 2/11/2020 4:30:00 AM, Open 1065, Low 1079.95 ...
3/11/2020 4:30:00 AM, Open 1072, Low 1079.4 ...
 

Примечание : существует также много способов обработки данных и настройки Json.net или TryParse дать вам желаемые результаты. Этот пример заключается в том, чтобы оставить это на ваше усмотрение и сосредоточиться исключительно на проекции.


Если вам нужно, чтобы это было сделано в конструкторе

 public struct Historical
{
   public Historical(JToken x)
   {
      TimeStamp = x[0].Value<DateTime>();
      Open = x[1].Value<decimal>();
      Low = x[2].Value<decimal>();
      High = x[3].Value<decimal>();
      Close = x[4].Value<decimal>();
      ...
   }
   public DateTime TimeStamp { get; }
   public decimal Open { get; }
   public decimal High { get; }
   public decimal Low { get; }
   public decimal Close { get; }
   ...     
}

...

var data = jtoken["data"]["candles"].Select(x => new Historical(x));
 

Комментарии:

1. Спасибо за ответ. Но есть ли способ сделать это без обновления свойства set исторического класса? Определение, которое я только что получил.