1

Currently, I am writing a C# wrapper for an API. It however, takes a currency, USD, GBP etc and converts data for the provided currency. When doing this, it also changes the name of some of the returned JSON values.

price_eur

price_usd

etc for example.

So my question would be, how can I work with this instead of writing hundreds of different classes for a couple of naming differences? I dont think a JsonProperty could fix this as the property needs to be a constant and that is about as far as my experience goes.

JSON to show how it differs:

Euro:

[
{
    "id": "bitcoin", 
    "name": "Bitcoin", 
    "symbol": "BTC", 
    "rank": "1", 
    "price_usd": "5648.67", 
    "price_btc": "1.0", 
    "24h_volume_usd": "1847890000.0", 
    "market_cap_usd": "93932015864.0", 
    "available_supply": "16629050.0", 
    "total_supply": "16629050.0", 
    "percent_change_1h": "-0.59", 
    "percent_change_24h": "-1.12", 
    "percent_change_7d": "16.59", 
    "last_updated": "1508253251", 
    "price_eur": "4803.1770744", 
    "24h_volume_eur": "1571297824.8", 
    "market_cap_eur": "79872271729.0"
}
]

GBP:

[
{
    "id": "bitcoin", 
    "name": "Bitcoin", 
    "symbol": "BTC", 
    "rank": "1", 
    "price_usd": "5648.67", 
    "price_btc": "1.0", 
    "24h_volume_usd": "1847890000.0", 
    "market_cap_usd": "93932015864.0", 
    "available_supply": "16629050.0", 
    "total_supply": "16629050.0", 
    "percent_change_1h": "-0.59", 
    "percent_change_24h": "-1.12", 
    "percent_change_7d": "16.59", 
    "last_updated": "1508253251", 
    "price_gbp": "4276.28608281", 
    "24h_volume_gbp": "1398932189.27", 
    "market_cap_gbp": "71110575085.0"
}
]

API in question: https://coinmarketcap.com/api/

Thanks!

  • You can use dictionary instead of class. `JsonConvert.DeserializeObject>>(json)` – Alexander Petrov Oct 17 '17 at 15:40
  • I tried that but received the classic "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.String]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly." Which would be because each key/value pair would need curly braces around it no? – GamingAnonymous Oct 17 '17 at 15:48
  • @GamingAnonymous, you can not deserialize into a Dictionary. Try a List> insetad, since the service returns a list of objects (as pointed out by Alexander Petrov) – taquion Oct 17 '17 at 15:53
  • You can also modify my solution to allow providing an array kind json string so that the output would be an array of wrappers – taquion Oct 17 '17 at 15:54
  • @AlexanderPetrov Thanks. Sorry, I misread your post. definitely the easiest answer – GamingAnonymous Oct 17 '17 at 16:01

2 Answers2

0

Since the changes are always the same and follow a consistent pattern you can try the following design to have only one class:

internal class CurrencyWrapper
    {
        public double MarketCap { get; private set; }

        public static CurrencyWrapper GetWrapper(string currencyCode, string jsonString)
        {
            var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
            var instance = new CurrencyWrapper
            {
                MarketCap = double.Parse(values["market_cap_" + currencyCode.ToLower()])
            };
            return instance;
        }
    }

Here are not included all error validations that must be done in a production environment.

Testing:

private static void Main(string[] args)
        {
            var eurStr = "{'market_cap_eur':'1233.22'}";
            var eurWrapper = CurrencyWrapper.GetWrapper("EUR", eurStr);

            var gbpStr = "{'market_cap_gbp':'123213213133.22'}";
            var gbpWrapper = CurrencyWrapper.GetWrapper("GBP", gbpStr);

            Console.WriteLine($"Eur market_whatever is: {eurWrapper.MarketCap}");
            Console.WriteLine($"GBP market_whatever is: {gbpWrapper.MarketCap}");
            Console.ReadLine();
        }

This way you can manage to map properties by using the currency code.

taquion
  • 2,667
  • 2
  • 18
  • 29
0

As I assume you want to have objects with set of properties which allow you to work with properties like id and name. And also you need a mechanism how to read custom properties from your classes. My option could look a bit tricky, so feel free to ask for further explanations.

Assume that we have 2 json strings:

private string input1 = "{\"id\": \"bitcoin\", \"name\": \"Bitcoin\", \"price_eur\": \"4803.1770744\"}";
private string input2 = "{\"id\": \"bitcoin\", \"name\": \"Bitcoin\", \"price_gbr\": \"4276.28608281\"}";

And we want to convert this strings into:

public interface IData
{
    string id { get; set; }
    string name { get; set; }
    string price { get; }
}

We can do it like this:

public class DynamicData : DynamicObject, IData
{
    private class FakeGetMemberBinder : GetMemberBinder
    {
        public FakeGetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase) { }

        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
        {
            throw new NotSupportedException();
        }
    }

    public string id { get; set; }

    public string name { get; set; }

    public string price => GetMemberWithSufix();

    public string CurrencySufix { get; set; }

    private readonly Dictionary<string, object> _dictionary = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return _dictionary.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        _dictionary[binder.Name] = value;
        return true;
    }

    public override IEnumerable<string> GetDynamicMemberNames() => _dictionary.Keys;

    private string GetMemberWithSufix([CallerMemberName] string callerMemberName = null)
    {
        var getBinder = new FakeGetMemberBinder(callerMemberName + "_" + CurrencySufix, false);
        object value;
        if (TryGetMember(getBinder, out value))
            return value?.ToString();

        throw new KeyNotFoundException();
    }
}

You can use this class like this:

var obj1 = JsonConvert.DeserializeObject<DynamicData>(input1);
obj1.CurrencySufix = "eur";
var obj2 = JsonConvert.DeserializeObject<DynamicData>(input2);
obj2.CurrencySufix = "gbr";

Debug.WriteLine(obj1.price);
Debug.WriteLine(obj2.price);

Output is

4803.1770744

4276.28608281

Valerii
  • 2,147
  • 2
  • 13
  • 27