Getting Errors when trying to Deserialize JSON object from API call in C#. (currency exchange) - c#

My goal is to build an API that can load currency data from https://openexchangerates.org/ api into our SQLite database. I am able to get the JSON object formatted like this:
When i run the code below, it returns an errors. I am not sure how to resolve this. Just need to convert this JSON into a C# class without error.
Here's the custom C# object to mapped to JSON obj:
public class ExchangeRate
{
public string Disclaimer { get; set; }
public string License { get; set; }
public string Timestamp { get; set; }
public string Base { get; set; }
public string Rates { get; set; }
}
Here is the api call where its returning the error:
public static async Task<List> GetLatest(string url)
{
var client = new HttpClient();
string results = await client.GetStringAsync(url);
List<ExchangeRate> ratesList = JsonConvert.DeserializeObject<List<ExchangeRate>>(results);
return ratesList;
}

The example JSON is not a list, it's a single object, this is specified in the exception message
...because the type requires a JSON array
, otherwise it would have [ ] around it indicating an array (can be deserialized to list). Also, your model is flawed as Rates is not a string, but an object, and Timestamp is not a string but a long for the datetime as ticks. Change your model like so:
public class ExchangeRate
{
//decorate your properties since the json string uses lowercase
[JsonProperty("disclaimer")]
public string Disclaimer { get; set; }
[JsonProperty("license")]
public string License { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
[JsonProperty("base")]
public string Base { get; set; }
[JsonProperty("rates")]
public Rates Rates { get; set; }
}
public class Rates
{
//create the properties for the Rates class
}
OR make the rates property a Dictionary<string, decimal>, NOTE: this could fail if any key is duplicated.
public class ExchangeRate
{
//decorate your properties since the json string uses lowercase
[JsonProperty("disclaimer")]
public string Disclaimer { get; set; }
[JsonProperty("license")]
public string License { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
[JsonProperty("base")]
public string Base { get; set; }
[JsonProperty("rates")]
public Dictionary<string, decimal> Rates { get; set; }
}
Change your code to this:
ExchangeRate rate = JsonConvert.DeserializeObject<ExchangeRate>(results);

Your C# class model does not match the incoming JSON data structure. Rates is an array of items, but you're treating it like a string in your C# model. Timestamp is a number but you're treating it like a string in your C# model.
public class ExchangeRate
{
public string Disclaimer { get; set; }
public string License { get; set; }
public int Timestamp { get; set; }
public string Base { get; set; }
public Dictionary<string, double> Rates { get; set; }
}

Related

How do I convert a JSON DateTime format to a C# DateTime format from an API call

I'm currently building a project that retrieves API data and saves it into a database. Everything is working fine except for the DateTime values in the API. I have a class that uses RestSharp to obtain the API data then it uses NewtonSoft.Json to derserialize the API data into a JSON format which is then stored into a temporary DTO class file. Here is the API method.
public static void getAllRequestData()
{
var client = new RestClient("[My API URL]");
var request = new RestRequest();
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
AllRequests.Rootobject result = JsonConvert.DeserializeObject<AllRequests.Rootobject>(rawResponse);
}
}
Now here is the DTO file (AllRequests) that will temporarily store the Converted JSON data.
public class AllRequests
{
public class Rootobject
{
public Operation Operation { get; set; }
}
public class Operation
{
public Result Result { get; set; }
public Detail[] Details { get; set; }
}
public class Result
{
public string Message { get; set; }
public string Status { get; set; }
}
public class Detail
{
[Key]
public int Id { get; set; }
public string Requester { get; set; }
public string WorkOrderId { get; set; }
public string AccountName { get; set; }
public string CreatedBy { get; set; }
public string Subject { get; set; }
public string Technician { get; set; }
public string IsOverDue { get; set; }
public string DueByTime { get; set; }
public string Priority { get; set; }
public string CreatedTime { get; set; }
public string IgnoreRequest { get; set; }
public string Status { get; set; }
}
}
The lines of code in Details that I want to be DateTime formats are "DueByTime" and "CreatedTime" instead of being String values. Currently they're only holding JSON format DateTime values in a String such as "1477394860065".
I've tried making "public string CreatedTime { get; set; }" to "public DateTime CreatedTime { get; set; }" However that only returned an error since it's JSON format. How could I rectify this issue so that it's stored in the DTO correctly in a DateTime format? Because ideally I want to scaffold this class file into a table so it can hold data in a database.
For more context to this, here's what I want rectified in my Database.
I want there to be a DateTime shown instead of a long list of numbers like there is here under Createby and DueBy.
Any help would be appreciated.
[EDIT] added the unix time format compliance[/EDIT]
Just putting in code what #Fildor said
public long CreatedTime { get; set; }
[JsonIgnore] // will ignore the property below when serializing/deserializing
public DateTimeOffset CreatedTimeDate {
// Don't need a setter as the value is directly get from CreatedTime property
get {
return DateTimeOffset.FromUnixTimeMilliseconds(CreatedTime);
}
}
Used also this answer to convert to DateTime as asked, using the local time.
Here is how you convert to DateTime if you don't need the offset : https://learn.microsoft.com/fr-fr/dotnet/standard/datetime/converting-between-datetime-and-offset

Converting JSON to Object fails - Cannot deserialize the current JSON object into System.Collections.Generic.List

I'm using the API of www.textlocal.in, which returns a JSON formatted object.
JSON
{
"warnings":[
{
"message":"Number is in DND",
"numbers":"917000000000"
}
],
"balance":900,
"batch_id":311110011,
"cost":1,
"num_messages":1,
"message":{
"num_parts":1,
"sender":"TXTLCL",
"content":"Test1"
},
"receipt_url":"",
"custom":"",
"inDND":[
"917000000000"
],
"messages":[
{
"id":"1350123781",
"recipient":918819437284
}
],
"status":"success"
}
My code with which I'm trying to parse the JSON:
private void button1_Click(object sender, EventArgs e)
{
var a = JsonConvert.DeserializeObject<List<jsonToObj[]>>(richTextBox1.Text);
}
public class jsonToObj
{
public warnings[] warnings { get; set; }
public int balance { get; set; }
public int batch_id { get; set; }
public int cost { get; set; }
public int num_messages { get; set; }
public message message { get; set; }
public string receipt_url { get; set; }
public string custom { get; set; }
public messages[] messages { get; set; }
public string status { get; set; }
}
public class warnings
{
public string message { get; set; }
public string numbers { get; set; }
}
public class messages
{
public string id { get; set; }
public int recipient { get; set; }
}
public class message
{
public int num_part { get; set; }
public string sender { get; set; }
public string content { get; set; }
}
I'm getting an exception with the following message:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the
current JSON object (e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[WindowsFormsApp1.Form2+jsonToObj[]]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly. To fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object. Path 'warnings', line 1, position
12.'
First of all you have to figure out what your API returns.
Right now you're trying to parse a List of jsonToObj Arrays (List<jsonToObj[]>). You have to decide whether to use a jsonToObj[] or List<jsonToObj> or a simple jsonToObj which your API provides now:
var a = JsonConvert.DeserializeObject<jsonToObj>(richTextBox1.Text);
But this then throws:
JSON integer 918819437284 is too large or small for an Int32. Path 'messages[0].recipient', line 25, position 33."
So make sure you use a Long for that.
public class messages
{
public string id { get; set; }
public long recipient { get; set; }
}
Furthermore you can add inDND to your jsonToObj class if you need the info:
public class jsonToObj
{
...
public string[] inDND { get; set; }
...
}
Based on string you class structure should be like this :
public class Warning
{
public string message { get; set; }
public string numbers { get; set; }
}
public class Message
{
public int num_parts { get; set; }
public string sender { get; set; }
public string content { get; set; }
}
public class Message2
{
public string id { get; set; }
public long recipient { get; set; }
}
public class RootObject
{
public List<Warning> warnings { get; set; }
public int balance { get; set; }
public int batch_id { get; set; }
public int cost { get; set; }
public int num_messages { get; set; }
public Message message { get; set; }
public string receipt_url { get; set; }
public string custom { get; set; }
public List<string> inDND { get; set; }
public List<Message2> messages { get; set; }
public string status { get; set; }
}
It looks like your class structure is not proper, Make use of visual studio and generate C# class from json string and then using that generated class try to deserialize class.
Read : Visual Studio Generate Class From JSON or XML
I simulated your problem and made the following changes that worked:
Change the method that deserializes to this:
var a = JsonConvert.DeserializeObject<jsonToObj>(richTextBox1.Text);
The result of the JSON you receive is not a List, so it will not work to deserialize to List<>.
The recipient property of the messages class receives values larger than an integer, so it must be transformed into a long like this:
public long recipient { get; set; }
These changes solve your problem.
Looks like this is a very old post, still thought of answering.
First of all, your Json data is singular which means, either
var a = JsonConvert.DeserializeObject<List<jsonToObj[]>>(richTextBox1.Text);
or
var a = JsonConvert.DeserializeObject<List<jsonToObj>>(richTextBox1.Text);
may not work for you.
You can either try:
var a = JsonConvert.DeserializeObject<jsonToObj>(richTextBox1.Text);
or
enclose the data with [ and ], which would do the trick.
make sure your parsing single object vs list of objects.

C# - Parsing JSON to multiple objects

I'm trying to create multiple objects out of a JSON result.
My JSON looks like that:
EDIT
[{"quality":"hd","type":"video/mp4","width":1920,"height":1080,"link":"http://player.vimeo.com/external/255898412.hd.mp4?s=8766561d230749d75a0ddde2ddfbeb69e0e5198e&profile_id=175&oauth2_token_id=1040381751","created_time":"2018-02-15T15:46:25+02:00","fps":23.98,"size":3113207678,"md5":"b6beed65b699df870e481045178accc5","link_secure":"https://player.vimeo.com/external/255898412.hd.mp4?s=8766561d230749d75a0ddde2ddfbeb69e0e5198e&profile_id=175&oauth2_token_id=1040381751"},{"quality":"sd","type":"video/mp4","width":640,"height":360,"link":"http://player.vimeo.com/external/255898412.sd.mp4?s=b51b55f6bd6e1af2a8f48571e5804d91e6a82533&profile_id=164&oauth2_token_id=1040381751","created_time":"2018-02-15T15:46:05+02:00","fps":23.98,"size":536864946,"md5":"af227a5526af15d2bce6ac951d6cf06b","link_secure":"https://player.vimeo.com/external/255898412.sd.mp4?s=b51b55f6bd6e1af2a8f48571e5804d91e6a82533&profile_id=164&oauth2_token_id=1040381751"},{"quality":"sd","type":"video/mp4","width":960,"height":540,"link":"http://player.vimeo.com/external/255898412.sd.mp4?s=b51b55f6bd6e1af2a8f48571e5804d91e6a82533&profile_id=165&oauth2_token_id=1040381751","created_time":"2018-02-15T15:46:05+02:00","fps":23.98,"size":1242328160,"md5":"1963f908509b14fd7a40dc46bfa6c519","link_secure":"https://player.vimeo.com/external/255898412.sd.mp4?s=b51b55f6bd6e1af2a8f48571e5804d91e6a82533&profile_id=165&oauth2_token_id=1040381751"},{"quality":"hd","type":"video/mp4","width":1280,"height":720,"link":"http://player.vimeo.com/external/255898412.hd.mp4?s=8766561d230749d75a0ddde2ddfbeb69e0e5198e&profile_id=174&oauth2_token_id=1040381751","created_time":"2018-02-15T15:46:05+02:00","fps":23.98,"size":1977386604,"md5":"af38f067bd39f4f5bb71bad72f925337","link_secure":"https://player.vimeo.com/external/255898412.hd.mp4?s=8766561d230749d75a0ddde2ddfbeb69e0e5198e&profile_id=174&oauth2_token_id=1040381751"},{"quality":"hls","type":"video/mp4","link":"https://player.vimeo.com/external/255898412.m3u8?s=f25b7114977a0c6b37739886da189051ed31999e&oauth2_token_id=1040381751","created_time":"2018-02-15T15:46:25+02:00","fps":23.98,"size":3113207678,"md5":"b6beed65b699df870e481045178accc5","link_secure":"https://player.vimeo.com/external/255898412.m3u8?s=f25b7114977a0c6b37739886da189051ed31999e&oauth2_token_id=1040381751"}]
it should be parsed into 3 objects.
I got a VideoFileModel.cs class that looks like that:
public partial class VideoFileModel
{
[JsonProperty("quality")]
public string Quality { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("width")]
public long? Width { get; set; }
[JsonProperty("height")]
public long? Height { get; set; }
[JsonProperty("link")]
public string Link { get; set; }
[JsonProperty("created_time")]
public System.DateTime CreatedTime { get; set; }
[JsonProperty("fps")]
public double Fps { get; set; }
[JsonProperty("size")]
public long Size { get; set; }
[JsonProperty("md5")]
public string Md5 { get; set; }
[JsonProperty("link_secure")]
public string LinkSecure { get; set; }
}
What I'm trying to do at the moment, is parsing it like that:
string json = Helpers.HTTPFetch(url, method, headers, body, contentType);
var result = JsonConvert.DeserializeObject<VideoFileModel>(json);
but I'm pretty sure it's not working, or at least not in the way I want it to work.
what should I change?
You should simply use List<> when deserializing :
var result = JsonConvert.DeserializeObject<List<VideoFileModel>>(json);
There are is a list of VideoFileModel in your JSON that you trying to deserialize into a single object.
Following gets deserialized:
UPDATE:
As per OP updated JSON. It still gets deserialized:

Type 'SalesforceDataQueryComponent.Utils.SfdcObjects+SfdcCollection is not supported for deserialization of an array

JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
// Deserialize the response to get an array of CUSTOM Cases
var reportsList = jsSerializer.Deserialize<SfdcObjects.SfdcCollection<SfdcObjects.Assets>>(HttpUtility.UrlDecode(response));
throws an exception:
Error: System.InvalidOperationException: Type 'SalesforceDataQueryComponent.Utils.SfdcObjects+SfdcCollection`1[
[SalesforceDataQueryComponent.Utils.SfdcObjects+Assets, SalesforceDataQueryComponent, Version=1.2.0.0, Culture=neutral]]'
is not supported for deserialization of an array.
I can not figure it out the issue:
Objects:
namespace SalesforceDataQueryComponent.Utils
{
class SfdcObjects
{
// Used for Authentication
public class TokenResponse
{
public string id { get; set; }
public string issued_at { get; set; }
public string refresh_token { get; set; }
public string instance_url { get; set; }
public string signature { get; set; }
public string access_token { get; set; }
}
// All classes shown next are used to parse the HttpGet Response
public class SfdcCollection<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
public class SfdcAttributes
{
public string Type { get; set; }
public string Url { get; set; }
}
public class Accounts : Account
{
public SfdcAttributes Attributes { get; set; }
}
public class Assets : Asset
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomAssets : Assets
{
public string StringInstallDate { get; set; }
}
public class Users : User
{
public SfdcAttributes Attributes { get; set; }
}
public class CustomCase : Case
{
public string StringCreatedDate { get; set; }
}
public class CustomCases : CustomCase
{
public SfdcAttributes Attributes { get; set; }
}
}
}
You do not include your response JSON in your question, however from the error message, your problem must be that the root JSON container in your response is an array. A JSON array, according to the JSON standard, looks like this:
[value1, value2, ..., valueN]
JSON serializers map types that implement ICollection or IEnumerable from and to JSON arrays.
Your root object SfdcCollection<T>, however, is NOT a collection or enumerable, despite its name. Instead it's a non-enumerable generic POCO:
public class SfdcCollection<T> // No IEnumerable<T>
{
public bool Done { get; set; }
public int Size { get; set; }
public string NextRecordsUrl { get; set; }
public List<T> Records { get; set; }
}
Thus a serializer will map this to a JSON object (which is a set of key/value pairs and looks like {"name1" : value1, "name2" : value2, ..., "nameN" : valueN }) instead of an array.
You need to update your data model to the JSON you are actually receiving. Try uploading your JSON to http://json2csharp.com/, it will automatically generate classes for you.
If you must use the classes in your question, you could ask another question about how to map the JSON you are actually receiving onto your required classes, using your desired serializer (e.g. Json.NET, DataContractJsonSerializer, JavaScriptSerializer, or etc.)

How to bind multilevel json data to a repeater in asp.net or converting json data to data table

I want to bind the Json data to the repeater I know only one process that is converting the Json data to data table and then binding data but here I am receiving multilevel json data i do't know how to convert them to data table
input json data:
{"apiAvailableBuses":
[{"droppingPoints":null,"availableSeats":40,"partialCancellationAllowed":false,"arrivalTime":"01:00 AM","cancellationPolicy":"[{\"cutoffTime\":\"1\",\"refundInPercentage\":\"10\"},{\"cutoffTime\":\"2\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"4\",\"refundInPercentage\":\"90\"}]","boardingPoints":[{"time":"07:40PM","location":"K.P.H.B,Beside R.S Brothers","id":"2238"}],"operatorName":"Apple I Bus","departureTime":"8:00 PM","mTicketAllowed":false,"idProofRequired":false,"serviceId":"6686","fare":"1000","busType":"Hi-Tech A/c","routeScheduleId":"6686","commPCT":9.0,"operatorId":203,"inventoryType":0},
{
"droppingPoints":null,"availableSeats":41,"partialCancellationAllowed":false,"arrivalTime":"06:00 AM","cancellationPolicy":"[{\"cutoffTime\":\"1\",\"refundInPercentage\":\"10\"},{\"cutoffTime\":\"2\",\"refundInPercentage\":\"50\"},{\"cutoffTime\":\"4\",\"refundInPercentage\":\"90\"}]","boardingPoints":[{"time":"08:00PM","location":"Punjagutta,","id":"2241"}],"operatorName":"Royalcoach Travels","departureTime":"8:00 PM","mTicketAllowed":false,"idProofRequired":false,"serviceId":"6736","fare":"800","busType":"VOLVO","routeScheduleId":"6736","commPCT":9.0,"operatorId":243,"inventoryType":0}
I am trying to convert it to data table by
public void getavailablebuses()
{
string url = string.Format(HttpContext.Current.Server.MapPath("files/getavailablebuses.json"));
using (WebClient client = new WebClient())
{
string json = client.DownloadString(url);
var result = JsonConvert.DeserializeObject<RootObject>(json);
string mm = JObject.Parse(json).SelectToken("apiAvailableBuses").ToString();
// var boardingpoint = JObject.Parse(mm).SelectToken("boardingPoints").ToString();
var Availablebuses = JObject.Parse(json).SelectToken("apiAvailableBuses").ToString();
DataTable dt = (DataTable)JsonConvert.DeserializeObject(Availablebuses, (typeof(DataTable)));
}
public class apiresult
{
public string message { get; set; }
public string success { get; set; }
}
public class RootObject
{
public apiresult apiStatus;
public List<apiAvailableBuses> apiAvailableBuses{ get; set; }
// public string apiAvailableBuses { get; set; }
}
public class apiAvailableBuses
{
public string serviceId { get; set; }
public string fare { get; set; }
public string busType { get; set; }
public string departureTime { get; set; }
public string operatorName { get; set; }
public string cancellationPolicy { get; set; }
public List<boardingpoints> boardingpoints { get; set; }
public string droppingPoints { get; set; }
public string inventoryType { get; set; }
public string routeScheduleId { get; set; }
public int availableSeats { get; set; }
public string arrivalTime { get; set; }
public Boolean idProofRequired { get; set; }
public Boolean partialCancellationAllowed { get; set; }
public int operatorId { get; set; }
public double commPCT { get; set; }
public string mTicketAllowed { get; set; }
}
public class boardingpoints
{
public string location { get; set; }
public string id { get; set; }
public string time { get; set; }
}
public class cancellationPolicy
{
public string cutoffTime { get; set; }
public string refundInPercentage { get; set; }
}
Here in the data table I am unable to get the boarding points, dropping points and cancellation policy
if I load cancellation policy as list or JObject I am getting error
so here I am loading cancellation policy as string.
but I am unable to load boarding points and dropping points.
Please help with this I am scratching my head from two days. Thanks in advance
"I know only one method to bind data to a repeater i.e data table." So this is a perfect opportunity to learn other ways, wouldn't you say?
Why don't you work with the result of JsonConvert.DeserializeObject<RootObject>(json);? This is a RootObject that has a property called apiAvailableBuses which seems to be exactly what you need to bind to your repeater, no?
By the way, a bit of code review:
apiresult and apiAvailableBuses violate Microsoft's rules WRT class names: those should be in PascalCase. Same for the properties of apiresult, e.g. message and success. Same for the properties of apiAvailableBuses.
RootObject has a public field: apiStatus. That probably needs to be a a property with a getter/setter.
Moreover, apiAvailableBuses is plural, which is incorrect, since the data therein is of only one bus. Same for boardingpoints: the class contains data for a single point, not multiple.
Be consistent: if you use string, then also use bool and not Boolean.

Categories

Resources