Search code examples
c#jsonjson-deserializationbinance-api-client

Binance API Invalid JSON Syntax on Kline/Candlestick data


Objective: Generate C# classes using JSON Utils for Binance REST API in order to deserialize data from exchange.

So far i have implemented all C# classes using JSON Utils in order to deserialize data from exhange. All except one (Kline/Candlestick data)

Problem: When i paste code from bellow in JSON Utils i am getting error: Invalid JSON Syntax

[
    [
        1660284300000,
        "323.50000000",
        "323.70000000",
        "322.40000000",
        "322.40000000",
        "757.16400000",
        1660284599999,
        "244731.13410000",
        536,
        "205.39900000",
        "66395.15700000",
        "0"
    ]
]

Is this valid JSON file and how to generate C# Class using this output in order to deserialize data?

EDIT

According to all your statements (esp. freakish), here is the source if anyone needed:

class B_KlineData
{
        public long openTime { get; set; }
        public string openPrice { get; set; }
        public string highPrice { get; set; }
        public string lowPrice { get; set; }
        public string closePrice { get; set; }
        public string volume { get; set; }
        public long closeTime { get; set; }
        public string quoteAssetVolume { get; set; }
        public long numberOfTrades { get; set; }
        public string baseVolume { get; set; }
        public string quoteVolume { get; set; }
        public string ignore { get; set; }

  
public static IEnumerable<B_KlineData> ParseBinanceResponse(string json)
        {
            var jsonDoc = JsonDocument.Parse(json);
            var root = jsonDoc.RootElement;

            foreach (var array in root.EnumerateArray())
            {
                yield return new B_KlineData
                {
                    openTime = array[0].GetInt64(),
                    openPrice = array[1].GetString(),
                    highPrice = array[2].GetString(),
                    lowPrice = array[3].GetString(),
                    closePrice = array[4].GetString(),
                    volume = array[5].GetString(),
                    closeTime = array[6].GetInt64(),
                    quoteAssetVolume = array[7].GetString(),
                    numberOfTrades = array[8].GetInt64(),
                    baseVolume = array[9].GetString(),
                    quoteVolume = array[10].GetString(),
                    ignore = array[11].GetString()
                };
            }

         }

}

CALLING Method

var jsonData= await market.KlineCandlestickData("BNBUSDT", Interval.FIVE_MINUTE,null,null,2 );
            
IEnumerable<B_KlineData> bdata = B_KlineData.ParseBinanceResponse(jsonData);

            foreach (var item in bdata)
            {
                txtLog.Text += item.openTime.ToString() + "\n";
                txtLog.Text += item.openPrice.ToString() + "\n";
                txtLog.Text += item.highPrice.ToString() + "\n";
                txtLog.Text += item.lowPrice.ToString() + "\n";
                txtLog.Text += item.closePrice.ToString() + "\n";
                txtLog.Text += item.volume.ToString() + "\n";
                txtLog.Text += item.closeTime.ToString() + "\n";
                txtLog.Text += item.quoteAssetVolume.ToString() + "\n";
                txtLog.Text += item.numberOfTrades.ToString() + "\n";
                txtLog.Text += item.baseVolume.ToString() + "\n";
                txtLog.Text += item.quoteVolume.ToString() + "\n";
                txtLog.Text += item.ignore.ToString() + "\n";

            }

What I'm not sure about is whether it is possible to optimize this code even more?


Solution

  • So Binance returns an array of arrays of mixed values. These are not random values though. Typically you would get an array of objects like

    [ { "openTime": 1660284300000, ... } ]

    but if the array is big then you pay the price for those keys, which are not really needed. You can choose to drop keys and turn that object into an array, where each index has a concrete meaning. And this is what Binance does.

    Unfortunately, typical automatic Json deserializers (for C# or not) don't work with such serialization variants. One way would be to deserialize this into object[][] double array, but this is awkward and also inefficient (boxing and other unnecessary allocations) and you still have to convert that double array into B_KlineData objects. The pro way is to do manual low level Json manipulation. Assuming you use System.Text.Json it can be done as follows:

    using System.Text.Json;
    
    public static IEnumerable<B_KlineData> ParseBinanceResponse(string json)
    {
        var jsonDoc = JsonDocument.Parse(json);
        var root = jsonDoc.RootElement;
    
        foreach (var array in root.EnumerateArray())
        {
            yield new B_KlineData
            {
                openTime = array[0].GetInt64(),
                open = array[1].GetString(),
                high = array[2].GetString(),
                low = array[3].GetString(),
                ...
            };
        }
    
    }
    

    So as you can see lots of manual work (you have to specify the meaning of each index manually). But you can't run away from it anyway, if ultimately you want to deserialize this into B_KlineData. Well, it could be automatized somewhat, but AFAIK no Json lib does this. I might be wrong though.

    Btw, your int fields should be long. And you can also add a proper validation inside ParseBinanceResponse function.

    Note that other packages (e.g. Newtonsoft.Json) should allow low level Json manipulation as well. The general idea stays the same though.