skip to Main Content

I have an issue with deserializing the JSON file from the web to a List.

My code is down below, but it does not execute and shows System.NullReferenceException. It happens in the Content() method, when I call callApiAsync() in the List.

class Program
{
    static void Main(string[] args)
    {
        Content();
        Console.ReadKey();
    }

    private async static void Content()
    {
        List<Coin> coins = await callApiAsync();

        for (int i = 0; i < coins.Count; i++)
        {
            Console.WriteLine(coins[i].price);
        }
    }

    static async Task<List<Coin>> callApiAsync()
    {
        string url = "https://api.coinstats.app/public/v1/charts?period=all&coinId=bitcoin";

        HttpClient httpClient = new HttpClient();

        var httpResponse = await httpClient.GetAsync(url);
        string jsonResponse = await httpResponse.Content.ReadAsStringAsync();

        var data = JsonConvert.DeserializeObject<Root>(jsonResponse);
        return data.coins;
    }
}

public class Root
{
    public List<Coin> coins { get; set; }
}

public class Coin
{
    public int time { get; set; }
    public int price { get; set; }
}

My JSON for example:

{
   "chart":[
      [
         1372032000,
         107.979,
         1,
         0
      ],
      [
         1372118400,
         102.982,
         1,
         0
      ],
      [
         1372204800,
         103.34,
         1,
         0
      ]
   ]
}

3

Answers


  1. The sample JSON you gave:

    {"chart":[[1372032000,107.979,1,0],[1372118400,102.982,1,0],[1372204800,103.34,1,0]]}
    

    Does not have a type that matches the "Root" class you are trying to deserialize to.

    You can paste your JSON into a site like https://json2csharp.com/ to find out what the corresponding C# class should look like. In this case, the Root class should look like the following to support deserializing the specified JSON:

    public class Root
    {
        public List<List<double>> chart { get; set; }
    }
    

    My guess is that you then intend to interpret the first value in each list as some sort of time stamp, the second value as a "price", and ignore the second and third values in each list. You will have to do the legwork for this after first deserializing the JSON to a list of lists of doubles.

    For example:

    var data = JsonConvert.DeserializeObject<Root>(jsonResponse);
    return data.chart.Select(listOfDoubles => new Coin
    {
        time = (int)listOfDoubles[0],
        price = listOfDoubles[1]
    }).ToList();
    
    Login or Signup to reply.
  2. You only need one string of code to deserialize

    return JObject.Parse(jsonResponse)["chart"]
    .Select(x => new Coin { time = UnixSecondsToDateTime( (long) x[0]), 
                            price = (decimal)x[1] }
            ).ToList();
    
    
    public class Coin
    {
        public DateTime time { get; set; }
        public decimal price { get; set; }
    }
    
    public static DateTime UnixSecondsToDateTime(long timestamp, bool local = false)
    {
        var offset = DateTimeOffset.FromUnixTimeSeconds(timestamp);
        return local ? offset.LocalDateTime : offset.UtcDateTime;
    }
    
    Login or Signup to reply.
  3. Unfortunately the API documentations do not tell anything about the response (1, 2).

    So, if we can assume that the first number represents an epoch timestamp and the second the average price then you can parse the response with Linq2Json like this

    var chart = JObject.Parse(jsonResponse)["chart"] as JArray;
    List<(int Time, int Price)> coins = new();
    foreach (JArray coin in chart)
    {
        coins.Add(((int)coin[0], (int)coin[1]));
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search