1

I have a response from some Rest API that just returns some array of arrays like the following:

[
[123,"0.01","0.02","0.03","0.04","12345.00000",123456789,"300.000",4000,"123.000","456.000","0"],
[456,"0.04","0.03","0.02","0.01","54321.00000",987654321,"500.000",4000,"123.000","456.000","1"],
[789,"0.05","0.06","0.07","0.08","12345.00000",123456789,"700.000",8000,"456.000","123.000","0"]    
]

In this example, the amount of datasets is 3, but the amount is always different and could be 100+ also.

I want to have this read out into a class object, which has 12 arrays according to each type of value shown in the response:

public class foo
{
    ...
    public int[] firstParam;
    public string[] secondParam;
    public string[] thirdParam;

    ...        
}

For example, firstParam should contain then {123,456,789}; secondParam should contain {"0.01","0.04","0.05"} and so on.

The schema for the columns is known and documented in Public Rest API for Binance: Kline/Candlestick data.. An example would be some query like https://api.binance.com/api/v1/klines?symbol=XVGBTC&interval=1h

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Try this link: https://stackoverflow.com/a/10432672/4180382 – Ole EH Dufour Jan 24 '18 at 19:03
  • 1
    The values with double quotes are floating point number, not strings. – jdweng Jan 24 '18 at 19:13
  • Do you know the schema of the individual columns? I.e that the first column is a start value, and so on? Also, what are you using for JSON parsing? Is it [tag:json.net]? – dbc Jan 24 '18 at 19:26
  • @ole: thanks for the link, but the recieved string ist not json compliant since there are no "keywords" or such. – Alexander S. Jan 24 '18 at 19:43
  • @AlexanderS. - It's perfectly well-formed JSON. Upload it to https://jsonlint.com/ and there are no errors. It's just that all the containers are arrays (ordered sequences of values) rather than objects (unordered sequences of name/value pairs). See http://www.json.org/ for confirmation. – dbc Jan 24 '18 at 19:47
  • @dbc: yes, schema is known. here is the documentation to it: [link](https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data) ... some part of that api has json formatted responses, but this one doesn't – Alexander S. Jan 24 '18 at 19:48
  • @dbc: just saw your last response - i guess you are right. seems to be json. do you know if json.net supports that kind of ordered sequences? i was going thru the doc, but just found things like "deserializing to objects" – Alexander S. Jan 24 '18 at 19:58
  • Yes it does. Also, which response is it? [Kline/Candlestick data](https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data)? – dbc Jan 24 '18 at 19:58
  • yes. it could be some query like https://api.binance.com/api/v1/klines?symbol=XVGBTC&interval=1h for example – Alexander S. Jan 24 '18 at 20:01

2 Answers2

3

The API response is perfectly valid JSON; it is a jagged 2d array of primitive values where the columns have specific meanings as defined in the Public Rest API for Binance: Kline/Candlestick data. As such it can be parsed and deserialized using , e.g. as an object [][]:

var arrays = JsonConvert.DeserializeObject<object [][]>(jsonString);

(Sample working .Net fiddle #1.)

However, rather than deserializing the JSON into a jagged 2d object array or (as you suggest in your question) a single root object with array properties corresponding to column values, I would recommend that you design a class BinanceKlineData that represents a single row of values for those specific columns, then deserialize into a List<BinanceKlineData> using the custom JsonConverter ObjectToArrayConverter<BinanceKlineData> from How to deserialize an array of values with a fixed schema to a strongly typed data class?.

Firstly, using the documented meanings of the columns, you can define your type BinanceKlineData as follows:

public class BinanceKlineData
{
    [JsonProperty(Order = 1)]
    public long OpenTime { get; set; }
    [JsonProperty(Order = 2)]
    public decimal Open { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 3)]
    public decimal High { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 4)]
    public decimal Low { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 5)]
    public decimal Close { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 6)]
    public decimal Volume { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 7)]
    public long CloseTime { get; set; }
    [JsonProperty(Order = 8)]
    public decimal QuoteAssetVolume { get; set; } // Or string, if you prefer
    [JsonProperty(Order = 9)]
    public long NumberOfTrades { get; set; } // Should this be an long or a decimal?
    [JsonProperty(Order = 10)]
    public decimal TakerBuyBaseAssetVolume { get; set; }
    [JsonProperty(Order = 11)]
    public decimal TakerBuyQuoteAssetVolume { get; set; }
    // public string Ignore { get; set; }
}

Notice that I have annotated the properties with [JsonProperty(Order = N)]. This order corresponds to the order of the columns in the Rest API, and will be used to inform Json.NET how to map columns to properties by column index. Notice also that I modeled the numeric columns as decimal despite the fact that they appear as strings in the JSON. You could use string if you prefer a more literal deserialization.

Next, grab the generic ObjectToArrayConverter<T> from this answer. It has logic to make use of the Order = N metadata to map row values to member values in the generic type T by column index.

Finally, you will be able to deserialize the JSON as a List<BinanceKlineData> as follows:

var settings = new JsonSerializerSettings
{
    Converters = { new ObjectToArrayConverter<BinanceKlineData>() },
};

var root = JsonConvert.DeserializeObject<List<BinanceKlineData>>(jsonString, settings);

Sample working .Net fiddle #2.

Or if you prefer you could apply the converter directly to BinanceKlineData as follows:

[JsonConverter(typeof(ObjectToArrayConverter<BinanceKlineData>))]
public class BinanceKlineData
{
    // Remainder as before
}

When the converter is applied directly to the type it is no longer necessary to pass it in via JsonSerializerSettings.Converters.

Sample fiddle #3.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • @AlexanderS. - you're welcome. If the question is answered please do [mark it as such](https://meta.stackexchange.com/a/147532/344280). – dbc Jan 25 '18 at 18:29
0

Depending on exactly how the response is formatted you might be able to do something like this:

var s = "[[123,\"0.01\",\"0.02\",\"0.03\",\"0.04\",\"12345.00000\",123456789,\"300.000\",4000,\"123.000\",\"456.000\",\"0\"],[456,\"0.04\",\"0.03\",\"0.02\",\"0.01\",\"54321.00000\",987654321,\"500.000\",4000,\"123.000\",\"456.000\",\"1\"],[789,\"0.05\",\"0.06\",\"0.07\",\"0.08\",\"12345.00000\",123456789,\"700.000\",8000,\"456.000\",\"123.000\",\"0\"]]";

var lines = s.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(b => b.TrimEnd('"').TrimStart('"')).ToArray()).Where(a => a.Any());

var c = lines.Count();

var foo = new foo
{
    firstParam = new int[c],
    secondParam = new string[c],
    thirdParam = new string[c]
};

for (int i = 0; i < c; i++)
{
    foo.firstParam[i] = Int32.Parse(lines.ElementAt(i)[0]);
    foo.secondParam[i] = lines.ElementAt(i)[1];
    foo.thirdParam[i] = lines.ElementAt(i)[2];
}

Console.WriteLine(string.Join(", ", foo.firstParam)); \\123, 456, 789
Console.WriteLine(string.Join(", ", foo.secondParam)); \\0.01, 0.04, 0.05
Console.WriteLine(string.Join(", ", foo.thirdParam)); \\0.02, 0.03, 0.06
SBFrancies
  • 3,987
  • 2
  • 14
  • 37