在C#中反序列化Kraken的OHLC JSON数据遇到问题
Hey there! Let's tackle this Kraken OHLC API deserialization issue you're having. I've dealt with similar quirks in Kraken's API responses before, so here's a step-by-step breakdown to get you sorted:
First, Understand the Kraken Response Structure
Unlike Cryptocompare's straightforward object-based JSON, Kraken returns OHLC data as arrays of values (no property names) and wraps the data under a dynamic key (e.g., XETHZUSD for your ETHUSD pair). A sample snippet looks like this:
{"error":[],"result":{"XETHZUSD":[[1699996800,"1800.10","1802.50","1799.80","1801.20","12.3456","1800.75"],...],"last":1699998600}}
This is the main reason your standard deserialization approach isn't working—you can't map anonymous arrays directly to a strongly-typed class without extra handling.
Step 1: Create Your C# Models
We'll need two classes: one to handle the top-level response, and another to represent a single OHLC candle. The key here is using JsonExtensionData (for System.Text.Json) or leveraging Newtonsoft.Json's JObject to handle the dynamic trading pair key.
For System.Text.Json:
using System.Text.Json.Serialization; public class OhlcCandle { public long Timestamp { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } public decimal Volume { get; set; } public decimal WeightedAverage { get; set; } } public class KrakenOhlcResponse { public List<string> Error { get; set; } = new List<string>(); public KrakenOhlcResult Result { get; set; } = new KrakenOhlcResult(); } public class KrakenOhlcResult { [JsonPropertyName("last")] public long LastUpdateTimestamp { get; set; } // Catch-all for the dynamic trading pair key [JsonExtensionData] public Dictionary<string, JsonElement> PairData { get; set; } = new Dictionary<string, JsonElement>(); }
For Newtonsoft.Json (Json.NET):
using Newtonsoft.Json; public class OhlcCandle { public long Timestamp { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } public decimal Volume { get; set; } public decimal WeightedAverage { get; set; } } public class KrakenOhlcResponse { public List<string> Error { get; set; } = new List<string>(); public KrakenOhlcResult Result { get; set; } = new KrakenOhlcResult(); } public class KrakenOhlcResult { [JsonProperty("last")] public long LastUpdateTimestamp { get; set; } // We'll handle the dynamic key via JObject parsing instead }
Step 2: Deserialize and Map the Data
Now we'll take the raw JSON response, deserialize it, then manually map the array values to your OhlcCandle class.
System.Text.Json Implementation:
using System.Text.Json; // Fetch the API response (use HttpClient in your actual code) var httpClient = new HttpClient(); var responseJson = await httpClient.GetStringAsync("https://api.kraken.com/0/public/OHLC?pair=ETHUSD&interval=30"); // Deserialize to our response model var krakenResponse = JsonSerializer.Deserialize<KrakenOhlcResponse>(responseJson); // Check for API errors first if (krakenResponse.Error.Any()) { Console.WriteLine($"Kraken API Error: {string.Join(", ", krakenResponse.Error)}"); return; } // Get the first (and only, since you requested one pair) set of OHLC data var ohlcArray = krakenResponse.Result.PairData .First() .Value .Deserialize<List<object[]>>(); // Map the array values to our OhlcCandle class var ohlcCandles = ohlcArray.Select(arr => new OhlcCandle { Timestamp = (long)arr[0], Open = decimal.Parse(arr[1].ToString()), High = decimal.Parse(arr[2].ToString()), Low = decimal.Parse(arr[3].ToString()), Close = decimal.Parse(arr[4].ToString()), Volume = decimal.Parse(arr[5].ToString()), WeightedAverage = decimal.Parse(arr[6].ToString()) }).ToList(); // Now you have a strongly-typed list of OHLC candles!
Newtonsoft.Json Implementation:
using Newtonsoft.Json.Linq; // Fetch the API response var httpClient = new HttpClient(); var responseJson = await httpClient.GetStringAsync("https://api.kraken.com/0/public/OHLC?pair=ETHUSD&interval=30"); // Parse to JObject for flexible handling var jObject = JObject.Parse(responseJson); // Check for errors var errors = jObject["error"].ToObject<List<string>>(); if (errors.Any()) { Console.WriteLine($"Kraken API Error: {string.Join(", ", errors)}"); return; } // Get the result object and find the trading pair key (skip the "last" property) var result = jObject["result"]; var pairKey = result.Properties().First(p => p.Name != "last").Name; var ohlcArray = result[pairKey].ToObject<List<object[]>>(); // Map to OhlcCandle var ohlcCandles = ohlcArray.Select(arr => new OhlcCandle { Timestamp = (long)arr[0], Open = decimal.Parse(arr[1].ToString()), High = decimal.Parse(arr[2].ToString()), Low = decimal.Parse(arr[3].ToString()), Close = decimal.Parse(arr[4].ToString()), Volume = decimal.Parse(arr[5].ToString()), WeightedAverage = decimal.Parse(arr[6].ToString()) }).ToList();
Key Notes to Remember
- Dynamic Pair Key: Kraken uses standardized pair codes (e.g.,
ETHUSDbecomesXETHZUSD), but since you're requesting a single pair, you can safely grab the first non-"last" property in the result. - Array Mapping: The OHLC data is ordered as
[timestamp, open, high, low, close, volume, weighted average]—make sure your index positions match this order. - Data Type Safety: Kraken returns numeric values as strings or floats, so parsing to
decimalensures you avoid floating-point precision issues.
内容的提问来源于stack exchange,提问作者Yeahson

