1

I'm currently using JSON.NET to get info from several web APIs, as explained to me in a previous q&a. But now I've stumbled upon another kind of web API that I can't parse because I don't know how to. This is the one: https://data.bter.com/api/1/tickers

As you can see, it's a json collection of trading pairs. But the collection itself is unnamed, so I'd have to make a class for each trading pair, which isn't really dynamic. I'm using the following to parse the url:

    public static T DownloadSerializedApi<T>(string address) where T : new()
    {
        T newT = new T();
        HttpClient client = new HttpClient();

        using (Stream s = client.GetStreamAsync(address).Result)
        using (StreamReader sr = new StreamReader(s))
        using (JsonReader reader = new JsonTextReader(sr))
        {
            JsonSerializer serializer = new JsonSerializer();

            newT = serializer.Deserialize<T>(reader);
        }

        return newT;
    }

Now I'd like to set T as class "TradingPairs" in which there would be a list with all tradingpairs. But the way I see it now, it will be a long list of hardcoded pairs :(

Anyone care to help me? ;)

3
  • Have you tried just using the code you have now? Json.Net shouldn't care if there's a type name. Commented Jun 21, 2014 at 1:49
  • That doesn't work. I can't make a "Dictionary<string, Pair> Pairs" as there is nothing that is called Pairs, but there has to be an easy way. If you throw that link into json2csharp.com you'll see that even there each pair gets its own class which isn't really efficient :( Commented Jun 21, 2014 at 2:09
  • You should be able to make a class Pair with the appropriate properties and map the json to those properties using the JsonProperty attribute. When I get back to my desk I'll provide a more complete solution. Commented Jun 21, 2014 at 3:47

2 Answers 2

2

The data appears to be a dictionary of objects. You should be returning a dictionary of stuff.

Though the "stuff" might be difficult to define. Fortunately, many of the properties of the "stuff" are common to all apparently. They only differ by two properties. And those properties seem to have names derived from the keys of the dictionary. Not the friendliest of formats but workable.

First we'll have to define the type:

public class Pair
{
    public Tuple<string, string> names { get; set; }
    public Tuple<decimal, decimal> vols { get; set; }

    // common properties
    public bool result { get; set; }
    public decimal last { get; set; }
    public decimal high { get; set; }
    public decimal low { get; set; }
    public decimal avg { get; set; }
    public decimal sell { get; set; }
    public decimal buy { get; set; }
}

And with a helper method:

private Pair CreatePair(string name1, string name2, JObject obj)
{
    var pair = obj.ToObject<Pair>();
    pair.names = Tuple.Create(name1, name2);
    // get the vols from the corresponding properties derived from the names
    pair.vols = Tuple.Create(
        obj.Value<decimal>("vol_" + name1),
        obj.Value<decimal>("vol_" + name2)
    );
    return pair;
}

You could do this:

var map = JsonConvert.DeserializeObject<JObject>(json);
var result =
    (from entry in map.Properties()
    let names = entry.Name.Split('_') // get the names
    let obj = entry.Value as JObject
    select CreatePair(names[0], names[1], obj))
    .ToDictionary(x => x.names);
Sign up to request clarification or add additional context in comments.

7 Comments

That's where I was heading as well, except the vol_btc member is named something different for every single entry. He might need to do Dictionary<string, Dictionary<string, object>> instead.
Well, they're all numbers. Not necessarily stored as a Number but json.net can tell the difference. Otherwise if that's really an issue and we need to accept random objects, you should prefer JObject.
result isn't a number.
I know you didn't. My point was if we use a strongly typed class Entry to represent the items, then we miss vol_btc for all items except one, because the name changes. And we can't use Dictionary<string, decimal> to represent the items because that doesn't work for result. So then we are left with Dictionary<string, object> (or Dictionary<string, JObject>, as you suggested).
Oh... I see now. But there's a pattern to this so it's not that tricky to adjust for it.
|
0

This JSON is a little tricky because of the changing property names, but you should still be able to use your generic method to get the data.

I think the approach I would take would be to make a class to represent a trading pair and use the [JsonExtensionData] feature to work around the fact that the vol_xxx property names change for each pair. Then deserialize to a Dictionary<string, TradingPair>.

Here's how you could define the class:

public class TradingPair
{
    public string result { get; set; }
    public decimal last { get; set; }
    public decimal high { get; set; }
    public decimal low { get; set; }
    public decimal avg { get; set; }
    public decimal sell { get; set; }
    public decimal buy { get; set; }

    [JsonExtensionData]
    private Dictionary<string, object> vols { get; set; }
}

Then deserialize the data like this:

var tradingPairs = DownloadSerializedApi<Dictionary<string, TradingPair>>(url);

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.