Complete Novice to JSON - this is my first foray into JSON with C#.
I have a single record returned from an HTTP get request.
var exchRateData = Get($"https://api.exchangeratesapi.io/2018-10-30?base=EUR&symbols=AUD");
Returns :
{"date":"2018-10-30","rates":{"AUD":1.6025},"base":"EUR"}
.
The AUD and 1.6025 values are both variables in the GET request. So the GET request can be for SGD or INR in which it returns:
{"date":"2018-10-30","rates":{"SGD":0.0187977737},"base":"INR"}
Always ONE record with levels is returned like those two examples above.
What I would like is to access the values of the second level values,
i.e {"AUD":1.6025} or {"SGD":0.0187977737}
I have tried:
var jsonResult = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(exchRateData);
foreach (var keyValue in jsonResult)
{
try
{
LblGetValues.Text = LblGetValues.Text + "Key: " + keyValue.Key + " Value:" + keyValue.Value + "<br>";
if (keyValue.Key == "date")
LblDate.Text = keyValue.Value;
//This is where I get stuck.
if (keyValue.Key == "rates")
{
// I would like to be able to do this here :
//string currencyCode = "AUD" (or "SGD" if second GET request)
//double currencyRate = 1.6025 (or 0.0187977737 if second GET request)
//JArray secondLevel = new JArray("rates");
// LblRate.Text = keyValue.Value.ToString();
}
if (keyValue.Key == "base")
LblBase.Text = keyValue.Value;
}
catch (Exception errMsg)
{
LblMsg.Text = errMsg.Message + errMsg.StackTrace;
}
}
Please help....
-
this might help - it has an answer with a nice extension method: stackoverflow.com/questions/16079116/…jazb– jazb2018年11月01日 08:12:48 +00:00Commented Nov 1, 2018 at 8:12
-
A little passive-aggressive there TheGeneral. ePossum, for the record though, a question that's easy to read is easier to answer, here's a link to the markdown for formatting questions you might have in future: stackoverflow.com/editing-helpLovethenakedgun– Lovethenakedgun2018年11月01日 08:21:30 +00:00Commented Nov 1, 2018 at 8:21
-
I can't see the "passive-aggressive" you refer to. I feel good this morning and I wont let this little snide spoil from @user3577502 spoil my day. Thanks Mihir Dave for your solution. Works like charm and you have made my day....ePossum– ePossum2018年11月01日 09:21:31 +00:00Commented Nov 1, 2018 at 9:21
-
@ePossum the accepted solution will work if you request 1 currency only, not multiple (e.g. api.exchangeratesapi.io/…). Check my answerRui Jarimba– Rui Jarimba2018年11月01日 09:43:17 +00:00Commented Nov 1, 2018 at 9:43
-
Yes @Rui Jarimba. You are right. However, I did accept this solution because we need only one rate for this project where. We need a conversion between two currency codes. The API also has options for multiple dates, date ranges etc and we did not want any scope creep by looking at solutions beyond returning a single exchange rate, However, your answer is useful because we will be embarking on currency triangulation after we finish the conversion project. The two projects are completely separate so no need to adopt same solution for both. I have marked up your answer of course.ePossum– ePossum2018年11月01日 10:08:29 +00:00Commented Nov 1, 2018 at 10:08
3 Answers 3
I would suggest using JSON.NET
Here you will deserialize your JSON string into your c# model class
your model class will look like this Model Generated using json2csharp
public class Rates
{
[JsonProperty(PropertyName = "fieldName")]
public double currency { get; set; }
}
public class RootObject
{
public string date { get; set; }
public Rates rates { get; set; }
public string @base { get; set; }
}
Then this is how you deserialize it using JSON.NET
// THis works for normal secanario but it won't work for you
var exchangeRates = JsonConvert.DeserializeObject<RootObject>("Your Json String goes here");
But then your JSON scenario is bit complex so you have to use CustomContractResolver
Here is Example for that
ContractResolver class
internal class CustomContractResolver : DefaultContractResolver
{
public CustomContractResolver(string fieldName)
{
FieldName = fieldName;
}
public string FieldName { get; set; }
protected override string ResolvePropertyName(string propertyName)
{
return propertyName == "fieldName" ? FieldName : base.ResolvePropertyName(propertyName);
}
}
how to use this setting
var settings = new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new CustomContractResolver("Pass Your currency here")
};
// Make Http Calls here and replace returned string in below method
var data = JsonConvert.DeserializeObject<RootObject>("Your JSON String", setting);
// Here You can access your currency rates
double currencyRate = data.currency;
1 Comment
The accepted solution works fine if there is only one rate (which is what the OP wants), but it won't work with more than 1 rate. Also, it adds extra complexity, which can be avoided by using a Dictionary
(see below).
Example - the following request (AUD
, USD
, GBP
)
https://api.exchangeratesapi.io/2018-10-30?base=EUR&symbols=AUD,USD,GBP
Will return the following json:
{"date":"2018-10-30","rates":{"USD":1.1372,"GBP":0.89148,"AUD":1.6025},"base":"EUR"}
You can use the following C# class to deserialize your json string:
public class ExchangeRate
{
[JsonProperty("date")]
public string Date { get; set; }
[JsonProperty("rates")]
public Dictionary<string, decimal> Rate { get; set; }
[JsonProperty("base")]
public string Base { get; set; }
}
Deserializing the json string:
string json = @"{""date"":""2018-10-30"",""rates"":{""USD"":1.1372,""GBP"":0.89148,""AUD"":1.6025},""base"":""EUR""}";
ExchangeRate rate = JsonConvert.DeserializeObject<ExchangeRate>(json);
Result:
1 Comment
public class Rates
{
public double AUD { get; set; }
}
public class ObjectCurrency
{
public string date { get; set; }
public Rates rates { get; set; }
public string @base { get; set; }
}
var data= JsonConvert.DeserializeObject<ObjectCurrency>(exchRateData);
5 Comments
JsonConvert.DeserializeObject<T>()
with the correct "type", as you've shown here. I'm not sure if this code will necessarily work for the OP as-is - he might need to change a few things. But it's definitely the correct approach.