Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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? ;)

share|improve this question
    
Have you tried just using the code you have now? Json.Net shouldn't care if there's a type name. –  jebar8 Jun 21 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 :( –  Bombadil Jun 21 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. –  jebar8 Jun 21 at 3:47

2 Answers 2

up vote 0 down vote accepted

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);
share|improve this answer

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);
share|improve this answer
    
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. –  Brian Rogers Jun 21 at 4:16
    
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. –  Jeff Mercado Jun 21 at 4:19
    
result isn't a number. –  Brian Rogers Jun 21 at 4:20
    
It's a good thing I didn't define it as a number... –  Jeff Mercado Jun 21 at 4:22
    
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). –  Brian Rogers Jun 21 at 4:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.