Home > Back-end >  Deserialize JSON with none constant key
Deserialize JSON with none constant key

Time:12-29

I'm trying to deserialize object using newtonesoft. The Json is a response from an API that convert currency by their updated rates. Problem is, that I have a key in the Json that is not constant. The problematic key is based on the request I'm sending (using GET), and the key is the currency I'm converting to. This is the Json response I'm getting from the API:

{
  "base_currency_code": "EUR",
  "base_currency_name": "Euro",
  "amount": "10.0000",
  "updated_date": "2022-12-28",
  "rates": {
    "GBP": {
      "currency_name": "Pound sterling",
      "rate": "0.8834",
      "rate_for_amount": "8.8336"
    }
  },
  "status": "success"
}

The fields I need from it are: 'base_currency_code' , 'GBP' and 'rate'. My issue is that the key 'GBP' is not the same and keeps on changing, for example the above response is a response where I've checked the conversion rates from USD to GBP, if I want to check USD to EUR the key will be named "EUR".

This is how I tried extracting the data:

            var result = JsonConvert.DeserializeObject<Data>(response);
            string from = result.base_currency_code;
            string to = result.rates.ToString().Substring(6,3);
            string rate = result.rates.rate;

and the classes:

    public class Rates
    {
        public string rate { get; set; }
        public object GBP { get; set; }

    }

    public class Data
    {
        public string base_currency_code { get; set; }
        public Rates rates { get; set; }
    }

right now everything is working, but if I change the "GBP" it will not work.. So basically my question is, how to deserialize a field that the name is always changing?

CodePudding user response:

The easiest way is to use a dictionary

    Data data = JsonConvert.DeserializeObject<Data>(response);

    double rate = data.Rates["GBP"].Rate; // 0.8834

IMHO since you are using c# , it is a good style to follow c# class property name rules, instead of a snake case

public partial class Data
{
    [JsonProperty("base_currency_code")]
    public string BaseCurrencyCode { get; set; }

    [JsonProperty("base_currency_name")]
    public string BaseCurrencyName { get; set; }

    [JsonProperty("amount")]
    public double Amount { get; set; }

    [JsonProperty("updated_date")]
    public DateTimeOffset UpdatedDate { get; set; }

    [JsonProperty("rates")]
    public Dictionary<string,RateAmount> Rates { get; set; }

    [JsonProperty("status")]
    public string Status { get; set; }
}

public partial class RateAmount
{
    [JsonProperty("currency_name")]
    public string CurrencyName { get; set; }

    [JsonProperty("rate")]
    public double Rate { get; set; }

    [JsonProperty("rate_for_amount")]
    public double RateForAmount { get; set; }
}

but if you dont know the currency code ("GBP" in this case) you need more complicated code to get data.

string currencyCode = data.Rates.FirstOrDefault().Key // "GBP"
double rate = data.Rates.FirstOrDefault().Value.Rate; // 0.8834

IMHO it is better to replace a dictionary with a special typed class Rates

     var jsonObj = JObject.Parse(json);
    Data data = jsonObj.ToObject<Data>();
    data.Rates = ( (JObject) jsonObj["rates"]).Properties().Select(p =>  new Rates { CurrencyCode=p.Name, RateAmount=p.Value.ToObject<RateAmount>()}).FirstOrDefault();
   
   string currencyCode = data.Rates.CurrencyCode;  // "GBP"
   double rate = data.Rates.RateAmount.Rate;     // 0.8834

public partial class Data
{
    // .... another properties

    public Rates Rates { get; set; }
}
    
public partial class Rates
{
    public string CurrencyCode {get;set;}
    public RateAmount RateAmount { get; set; }
}

CodePudding user response:

What @dbc said in a comment on my post resolved my issue. All I needed to do is create a dictionary.

Before change:

    public class Rates
    {
        public string rate { get; set; }
        public object GBP { get; set; }

    }

    public class Data
    {
        public string base_currency_code { get; set; }
        public Rates rates { get; set; }
    }

After:

    public class Rates
    {
        [JsonProperty("currency_name")]
        public string CurrencyName { get; set; }
        [JsonProperty("rate")]
        public string Rate { get; set; }
        [JsonProperty("rate_for_amount")]
        public string RateForAmount { get; set; }

    }

    public class Data
    {
        [JsonProperty("base_currency_code")]
        public string BaceCurrencyCode { get; set; }
        [JsonProperty("rates")]
        public Dictionary<string,Rates> Rates { get; set; }
    }

This way I can reach the data I need. Also thanks to @Serge for pointing out that for best parctice use C# class property name rules.

  • Related