skip to Main Content

this question is almost identical to the one here

I have tried translating the answer into C#, but I am not an expert in JSON and am a little lost.

I am attempting to deserialize this JSON response from the Kraken OHLC endpoint. Example response, click here

The request pulls back this JSON:

{
"error":[],

"result":
        {
            "XXBTZEUR":
                    [
                        [1679269500,"26401.1","26401.1","26211.6","26243.9","26307.1","8.92959425",311],
                        [1679270400,"26253.1","26324.7","26060.2","26242.9","26212.0","33.15872129",520],
                        [1679271300,"26250.1","26276.8","26216.8","26260.4","26259.0","3.63710383",183]
                    ],
                
            "last":1679915700

        }}

It’s valid JSON, but I cannot figure out how to handle the "last" field, which seems to throw everything out.

I thought I was close with these Class structures, but no matter what, my inner array returns as null:

public class OHLCResponse
{
    [JsonProperty(PropertyName = "error")]
    public List<string> Error;

    [JsonProperty("result")]
    public OHLC Result { get; set; }
}

public class OHLC
{
    //[JsonProperty("ohlc_pairs")]
    public Dictionary<string, List<OHLCPair>> OHLCPairs { get; set; }

    [JsonProperty("last")]
    public int Last { get; set; }
}

public class OHLCPair 
{
    [JsonProperty("time")]
    public int UnixDateStamp { get; set; }
    [JsonProperty("open")]
    public string Open { get; set; }
    [JsonProperty("high")]
    public string High { get; set; }
    [JsonProperty("low")]
    public string Low { get; set; }
    [JsonProperty("close")]
    public string Close { get; set; }
    [JsonProperty("vwap")]                  //volume weighted average price
    public string VWAP { get; set; }
    [JsonProperty("volume")]
    public string Volume { get; set; }
    [JsonProperty("count")]
    public string Count { get; set; }
}

And I’m just calling it with a standard library that is happily handling most of the other objects into type-safe Classes in their API:

OHLCResponse cOHLCResponse = _CKraken.GetPublicWebResponse<OHLCResponse>("OHLC", param);

… which implements:

result = (T)JsonConvert.DeserializeObject<T>(json);

The result, no matter how I try to alter my type Classes is always an empty array, because I think that it just cannot handle the "last" field:

enter image description here

Can anyone point me in the right direction? I was unfortunately not able to translate the custom deserializer in the previous question. Many thanks in advance, Dave.

2

Answers


  1. Wow, quite a challenging question.

    I think you can achieve this by implementing the custom JSON converter for the OHLC and OHLCPair classes.

    1. Register the JsonConverter with the JsonConverter attribute for the respective property.
    public class OHLCResponse
    {   
        [JsonProperty(PropertyName = "error")]
        public List<string> Error;
    
        [JsonProperty(PropertyName = "result")]
        [JsonConverter(typeof(OHLCConverter))]
        public OHLC Result { get; set; }
    }
    
    public class OHLC
    {
        public OHLC() { }
            
        [JsonProperty("ohlc_pairs")]
        public Dictionary<string, List<OHLCPair>> OHLCPairs { get; set; }
    
        [JsonProperty("last")]
        public long Last { get; set; }
    }
    
    [JsonConverter(typeof(OHLCPairConverter))]
    public class OHLCPair 
    {
        public OHLCPair() { }
    
        
        [JsonProperty("time")]
        public long UnixDateStamp { get; set; }
        [JsonProperty("open")]
        public string Open { get; set; }
        [JsonProperty("high")]
        public string High { get; set; }
        [JsonProperty("low")]
        public string Low { get; set; }
        [JsonProperty("close")]
        public string Close { get; set; }
        [JsonProperty("vwap")]                  //volume weighted average price
        public string VWAP { get; set; }
        [JsonProperty("volume")]
        public string Volume { get; set; }
        [JsonProperty("count")]
        public int Count { get; set; }
    }
    
    • In OHLCPairConverter, it aims to convert the key-value pair other than the last field to Dictionary<string, List<OHLCPair>> and assign it to the OHLCPairs property.

    • In OHLCPairConverter, it aims to convert the array (JArray) with multiple values and types to an OHLCPair instance.

    public class OHLCConverter : JsonConverter<OHLC>
    {
        public override void WriteJson(JsonWriter writer, OHLC value, JsonSerializer serializer)
        {
            JsonSerializerSettings settings = new JsonSerializerSettings
            {
                Converters = serializer.Converters.Where(s => !(s is OHLCConverter)).ToList(),
                DateFormatHandling = serializer.DateFormatHandling,
                MissingMemberHandling = serializer.MissingMemberHandling,
                NullValueHandling = serializer.NullValueHandling,
                Formatting = serializer.Formatting
            };
            var localSerializer = JsonSerializer.Create(settings);
            
            writer.WriteStartObject();
            
            foreach (PropertyInfo propInfo in typeof(OHLC).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty))
            {
                var jsonPropAttr = propInfo.GetCustomAttributes<JsonPropertyAttribute>()
                    .FirstOrDefault();
                var propName = jsonPropAttr != null
                    ? jsonPropAttr.PropertyName
                    : propInfo.Name;
                    
                writer.WritePropertyName(propName);
                
                if (propInfo.Name == "OHLCPairs")
                {
                    var jObject = JObject.FromObject(propInfo.GetValue(value), localSerializer);
                    jObject.WriteTo(writer);
                }
                else
                {
                    writer.WriteValue(propInfo.GetValue(value));
                }
            }
            
            writer.WriteEndObject();
        }
    
        public override OHLC ReadJson(JsonReader reader, Type objectType, OHLC existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            JObject obj = JObject.FromObject(token);
            Dictionary<string, List<OHLCPair>> ohlcPairs = null;
            
            if (token.Type != JTokenType.Null)
            {
                    ohlcPairs = obj.ToObject<Dictionary<string, object>>()
                            .Where(x => x.Key != "last")
                            .ToDictionary(x => x.Key, 
                                          x => JArray.FromObject(x.Value)
                                            .Children<JArray>()
                                            .Select(x => JsonConvert.DeserializeObject<OHLCPair>(x.ToString(), new JsonSerializerSettings
                                                                       {
                                                                            Converters = new List<JsonConverter> { new OHLCPairConverter() }   
                                                                       }))
                                            .ToList());
            }
        
            return new OHLC
            {
                OHLCPairs = ohlcPairs,
                Last = obj["last"].ToObject<int>()
            };
        }
    }
    
    public class OHLCPairConverter : JsonConverter<OHLCPair>
    {
        public override void WriteJson(JsonWriter writer, OHLCPair value, JsonSerializer serializer)
        {
            writer.WriteStartObject();
            
            foreach (PropertyInfo propInfo in typeof(OHLCPair).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty))
            {
                var jsonPropAttr = propInfo.GetCustomAttributes<JsonPropertyAttribute>()
                    .FirstOrDefault();
                var propName = jsonPropAttr != null
                    ? jsonPropAttr.PropertyName
                    : propInfo.Name;
                    
                writer.WritePropertyName(propName);
                writer.WriteValue(propInfo.GetValue(value));
            }
                    
            writer.WriteEndObject();
        }
    
        public override OHLCPair ReadJson(JsonReader reader, Type objectType, OHLCPair existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            JArray jArray = JArray.FromObject(token);
            
            return new OHLCPair
            {
                UnixDateStamp = jArray[0].ToObject<int>(),
                Open = jArray[1].ToObject<string>(),
                High = jArray[2].ToObject<string>(),
                Low = jArray[3].ToObject<string>(),
                Close = jArray[4].ToObject<string>(),
                VWAP = jArray[5].ToObject<string>(),
                Volume = jArray[6].ToObject<string>(),
                Count = jArray[7].ToObject<int>()
            };
        }
    }
    

    Anyway, for the WriteJson methods, you can achieve to write JSON (serialize) by deciding the format, the property, and its value to be printed without the need of System.Reflection. My approach seems to over-complicated.

    Login or Signup to reply.
  2. you just need a very simple converter

    using Newtonsoft.Json;
    
    OHLCResponse oHLCResponse = JsonConvert.DeserializeObject<OHLCResponse>(json, new OHLCConverter());
        
    public class OHLCConverter : JsonConverter<OHLCResponse>
    {
        public override OHLCResponse ReadJson(JsonReader reader, Type objectType, OHLCResponse existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            var jObj = JObject.Load(reader);
            var props = ((JObject)jObj["result"]).Properties().ToArray();
            var dict = new Dictionary<string, List<OHLCPair>>();
    
            for (int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (prop.Value.Type != JTokenType.Array) continue;
    
                var arr = new JArray( prop.Value.Select(v => new JObject(
                         v.Select((x, index) => new JProperty(index.ToString(),x)))) );
                                    
                dict.Add(prop.Name, arr.ToObject<List<OHLCPair>>());
                prop.Remove();
            }
            OHLCResponse oHLCResponse = jObj.ToObject<OHLCResponse>();
            oHLCResponse.Result.OHLCPairs = dict;
            return oHLCResponse;
        }
    
        public override void WriteJson(JsonWriter writer, OHLCResponse value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    and change JsonProperty attributes of OHLCPair class

    public class OHLCPair
    {
        [JsonProperty("0")]
        public int UnixDateStamp { get; set; }
        [JsonProperty("1")]
        public string Open { get; set; }
        [JsonProperty("2")]
        public string High { get; set; }
        [JsonProperty("3")]
        public string Low { get; set; }
        [JsonProperty("4")]
        public string Close { get; set; }
        [JsonProperty("5")]
        public string VWAP { get; set; }
        [JsonProperty("6")]
        public string Volume { get; set; }
        [JsonProperty("7")]
        public int Count { get; set; }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search