2
\$\begingroup\$

So I got kind of a weird format returned from the api I'm calling it's the following:

[
    [
        1609065000000,
        "22739.71000000",
        "22795.73000000",
        "22700.39000000",
        "22779.08000000",
        "38.81782800",
        1609065899999,
        "883167.94526884",
        2261,
        "18.58965200",
        "423036.58181400",
        "0"
    ]
]
    public static class StringExtensions
    {
        public static IEnumerable<Candlestick> ToCandleStickList(this string jsonObject)
        {
            var candleStickList = new List<Candlestick>();
            var list = JsonConvert.DeserializeObject<List<object[]>>(jsonObject);
            foreach (var obj in list)
            {
                candleStickList.Add(new Candlestick
                {
                    OpenTime = (long)obj[0],
                    Open = double.Parse((string)obj[1]),
                    High = double.Parse((string)obj[2]),
                    Low = double.Parse((string)obj[3]),
                    Close = double.Parse((string)obj[4]),
                    Volume = double.Parse((string)obj[5]),
                    CloseTime = (long)obj[6],
                    QuoteAssetVolume = double.Parse((string)obj[7]),
                    NumberOfTrades = (long)obj[8],
                    TakerBuyBaseAssetVolume = double.Parse((string)obj[9]),
                    TakerBuyQuoteAssetVolume = double.Parse((string)obj[10])
                });
            }

            return candleStickList;
        }
    }
    public class Candlestick
    {
        public long OpenTime { get; set; }
        public double Open { get; set; }
        public double High { get; set; }
        public double Low { get; set; }
        public double Close { get; set; }
        public double Volume { get; set; }
        public long CloseTime { get; set; }
        public double QuoteAssetVolume { get; set; }
        public long NumberOfTrades { get; set; }
        public double TakerBuyBaseAssetVolume { get; set; }
        public double TakerBuyQuoteAssetVolume { get; set; }
    }

I use the static method as follows

public async Task<IEnumerable<Candlestick>> GetCandleSticks(string symbol, string interval)
        {
            var endpoint = $"api/v3/klines?symbol={symbol}&interval={interval}";
            var response = await _httpClient.GetAsync(endpoint);

            if (response.IsSuccessStatusCode)
            {
                var result = (await response.Content.ReadAsStringAsync()).ToCandleStickList();

                return result;
            }

            throw new Exception($"");
        }

Does anyone got any suggestions for better code? Thanks in advance!

\$\endgroup\$
3
  • \$\begingroup\$ Does your code work as expected. We only review working code. \$\endgroup\$
    – pacmaninbw
    Dec 27, 2020 at 12:20
  • \$\begingroup\$ My code works as expected yeah. \$\endgroup\$
    – Mounir
    Dec 27, 2020 at 12:40
  • \$\begingroup\$ @pacmaninbw I was looking for a better way to cast the string to an object. Or overall code smells that would get noticed. I think I clarified that in the last sentece of my question? \$\endgroup\$
    – Mounir
    Dec 27, 2020 at 13:15

2 Answers 2

3
\$\begingroup\$

Json.NET has a very useful JsonConverter which you can use to add a custom JsonConverter. Which what you actually need.

So, you can do something like this :

public class CandlestickConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Candlestick);
    }

    public override object ReadJson(JsonReader reader , Type objectType , object existingValue , JsonSerializer serializer)
    {
        if(reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        var candle = ( existingValue as Candlestick ?? new Candlestick() );

        var array = JArray.Load(reader);

        candle.OpenTime = array.ElementAtOrDefault(0)?.ToObject<long>(serializer) ?? 0;
        candle.Open = array.ElementAtOrDefault(1)?.ToObject<double>(serializer) ?? 0;
        candle.High = array.ElementAtOrDefault(2)?.ToObject<double>(serializer) ?? 0;
        candle.Low = array.ElementAtOrDefault(3)?.ToObject<double>(serializer) ?? 0;
        candle.Close = array.ElementAtOrDefault(4)?.ToObject<double>(serializer) ?? 0;
        candle.Volume = array.ElementAtOrDefault(5)?.ToObject<double>(serializer) ?? 0;
        candle.CloseTime = array.ElementAtOrDefault(6)?.ToObject<long>(serializer) ?? 0;
        candle.QuoteAssetVolume = array.ElementAtOrDefault(7)?.ToObject<double>(serializer) ?? 0;
        candle.NumberOfTrades = array.ElementAtOrDefault(8)?.ToObject<long>(serializer) ?? 0;
        candle.TakerBuyBaseAssetVolume = array.ElementAtOrDefault(9)?.ToObject<double>(serializer) ?? 0;
        candle.TakerBuyQuoteAssetVolume = array.ElementAtOrDefault(10)?.ToObject<double>(serializer) ?? 0;

        return candle;
    }

    public override void WriteJson(JsonWriter writer , object value , JsonSerializer serializer)
    {
        var candle = (Candlestick) value;
        serializer.Serialize(writer ,
            new[]
            {
                candle.OpenTime,
                candle.Open,
                candle.High,
                candle.Low,
                candle.Close,
                candle.Volume,
                candle.CloseTime,
                candle.QuoteAssetVolume,
                candle.NumberOfTrades,
                candle.TakerBuyBaseAssetVolume,
                candle.TakerBuyQuoteAssetVolume
            });
    }
}

Now, using this custom converter you can use the JsonConverter attribute on the class like this :

[JsonConverter(typeof(CandlestickConverter))]
public class Candlestick
{
    ...
}

Now you can do this directly :

var candles = JsonConvert.DeserializeObject<List<Candlestick>>(jsonObject);

So, you don't need to use extensions, you only need to do a custom converter which is the proper way to handle Json serialization when using Json.NET.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks I'll give this a try and keep the post updated. \$\endgroup\$
    – Mounir
    Dec 27, 2020 at 21:05
  • \$\begingroup\$ I gave it a try and ran in following error Cannot deserialize the current JSON array (e.g. [1,2,3]) into type Candlestick because the type requires a JSON object (e.g. {"name":" value"}) to deserialize correctly.. I'll post my question about stackoverflow about this question. But to clarify the refactoring didn't exactly work as expected. \$\endgroup\$
    – Mounir
    Dec 28, 2020 at 12:37
  • \$\begingroup\$ stackoverflow.com/questions/65477922/… \$\endgroup\$
    – Mounir
    Dec 28, 2020 at 12:54
  • \$\begingroup\$ Resolved and the code is a lot cleaner now thanks for the code review. \$\endgroup\$
    – Mounir
    Dec 28, 2020 at 13:20
1
\$\begingroup\$

A few quick observations:

String extensions is generally a bad idea because string is such a common type.

Conversion code can be written more defensively. Use TryParse instead of Parse and if TryParse fails then handle that by throwing an Exception, or some other error handling.

Throwing a generic exception with no message is not great.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you very much for the quick observations, I'll try and use TryParse instead of Parse. Do you have a recommendation what to use instead of using a string extensions? \$\endgroup\$
    – Mounir
    Dec 27, 2020 at 19:20
  • \$\begingroup\$ @MounirMehjoub a local method inside GetCandleSticks could be an idea. \$\endgroup\$ Dec 27, 2020 at 19:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.