简体   繁体   中英

C# Getting unnamed list from json web api using JSON.NET

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM