Calling an Api multiple times C# - c#

I am trying to get use the rawg api and get games from their api, i have created the game entity class to parse the data to
//game entity
public class Game
{
public int Id { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Released { get; set; }
public string Background_Image { get; set; }
public int Rating { get; set; }
public int Playtime { get; set; }
}
now i need to get the games from the api, and i made this method
public static async Task<List<Game>> ApiRawgGamesRequest()
{
var gamesList = new List<Game>();
for (int i = 1; i < 250; i++)
{
using (var msg = new HttpRequestMessage(HttpMethod.Get, new Uri($"https://api.rawg.io/api/games?page_size=40&page={i}")))
using (var response = await _client.SendAsync(msg))
{
response.EnsureSuccessStatusCode();
var gamesResponse = await response.Content.ReadAsAsync<Game[]>();
gamesList.AddRange(gamesResponse);
}
}
return gamesList;
}
i am trying not to add all the games at once so i used a for loop to continously add the games into a list to potentially reduce the stress.
but i get an error
// error
Unhandled exception. System.AggregateException: One or more errors occurred. (Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Domain.Game[]' because the t
ype 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<T>) 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 'count', line 1, position 9.)
the response from the rawg api looks like this -- the results part is the game data i am trying to collect
// api response
{
"count": 454988,
"next": "https://api.rawg.io/api/games?page=2&page_size=40",
"previous": null,
"results": [
{
"id": 3498,
"slug": "grand-theft-auto-v",
"name": "Grand Theft Auto V",
"released": "2013-09-17",
"tba": false,
"background_image": "https://media.rawg.io/media/games/84d/84da2ac3fdfc6507807a1808595afb12.jpg",
"rating": 4.48,
},
{
"id": 4200,
"slug": "portal-2",
"name": "Portal 2",
"released": "2011-04-18",
"tba": false,
"background_image": "https://media.rawg.io/media/games/328/3283617cb7d75d67257fc58339188742.jpg",
"rating": 4.62
}
]
}
is there a better way to do this?

Your exception is resulting from a wrong JSON parsed object.
In your code you are calling:
var gamesResponse = await response.Content.ReadAsAsync<Game[]>();
But the response is the object (from your example JSON):
public class GameResponse
{
public int Count { get; set; }
public string Next{ get; set; }
public string Previous { get; set; }
public IEnumerable<Game> Results { get; set; }
}
If you change it to:
var gamesResponse = await response.Content.ReadAsAsync<GameResponse>();
You will be successful in parsing the response and access the games via:
gamesResponse.Results

So, you are trying to deserialize only the "results" part of the json, you have to build a class with all the items from the response json.
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
public class Result {
public int id { get; set; }
public string slug { get; set; }
public string name { get; set; }
public string released { get; set; }
public bool tba { get; set; }
public string background_image { get; set; }
public double rating { get; set; }
}
public class Root {
public int count { get; set; }
public string next { get; set; }
public object previous { get; set; }
public List<Result> results { get; set; }
}
You can always use some kind of Json>class generator online, used this for this anwser: https://json2csharp.com/

There are a couple of good answers addressing the issue of not having the correct structure. The only thing I am adding here as a good practice and for better control of your variable names are the annotations. Note, DataContract and DataMember are part of the framework but work well with Json.Net even though Json.Net comes with their own set of annotations.
[DataContract]
public class Payload
{
[DataMember(Name = "count")]
public int Count { get; set; }
[DataMember(Name = "next")]
public string Next { get; set; }
[DataMember(Name = "previous")]
public string Previous { get; set; }
[DataMember(Name = "results")]
public List<GameData> Results { get; set; }
}
[DataContract]
public class GameData
{
[DataMember(Name = "id")]
public long Id { get; set; }
[DataMember(Name = "slug")]
public string Slug { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "rating")]
public float Rating { get; set; }
[DataMember(Name = "rating_top")]
public float Rating_top { get; set; }
}
public static async Task<IEnumerable<Payload>> GetGameDatasAsync(int page = 1, int pageSize = 40, bool followNext = false, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
string next = $"https://api.rawg.io/api/games?page_size={pageSize}&page={page}";
var results = new List<Payload>();
using (var client = new HttpClient())
do
{
var result = await client.GetAsync(next, cancellationToken);
next = string.Empty;
if (result.IsSuccessStatusCode)
{
var json = await result.Content.ReadAsStringAsync();
if (!string.IsNullOrWhiteSpace(json))
{
var payload = JsonConvert.DeserializeObject<Payload>(json);
next = payload.Next;
results.Add(payload);
}
}
} while (!string.IsNullOrWhiteSpace(next) && followNext);
return results;
}

Related

Deserialize JSON using specific properties

I'm trying to deserialize JSON without declaring every property in C#. Here is a cut-down extract of the JSON:
{
"resourceType": "export",
"type": "search",
"total": 50,
"timestamp": "2020-08-02T18:26:06.747+00:00",
"entry": [
{
"url": "test.com/123",
"resource": {
"resourceType": "Slot",
"id": [
"123"
],
"schedule": {
"reference": {
"value": "testvalue"
}
},
"status": "free",
"start": "2020-08-03T08:30+01:00",
"end": "2020-08-03T09:00+01:00"
}
}
]
}
I want to get the values out of entry → resource, id and start.
Any suggestions on the best way to do this?
I've made very good experiences with json2sharp. You can enter your JSON data there and it will generate the classes you need to deserialize the JSON data for you.
public class Reference
{
public string value { get; set; }
}
public class Schedule
{
public Reference reference { get; set; }
}
public class Resource
{
public string resourceType { get; set; }
public List<string> id { get; set; }
public Schedule schedule { get; set; }
public string status { get; set; }
public string start { get; set; }
public string end { get; set; }
}
public class Entry
{
public string url { get; set; }
public Resource resource { get; set; }
}
public class Root
{
public string resourceType { get; set; }
public string type { get; set; }
public int total { get; set; }
public DateTime timestamp { get; set; }
public List<Entry> entry { get; set; }
}
The next step is to choose a framework which will help you to deserialize. Something like Newtonsoft JSON.
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
If you want to get the data without declaring classes, you can use Json.Net's LINQ-to-JSON API (JToken, JObject, etc.). You can use the SelectToken method with a JsonPath expression to get what you are looking for in a couple of lines. Note that .. is the recursive descent operator.
JObject obj = JObject.Parse(json);
List<string> ids = obj.SelectToken("..resource.id").ToObject<List<string>>();
DateTimeOffset start = obj.SelectToken("..resource.start").ToObject<DateTimeOffset>();
Working demo here: https://dotnetfiddle.net/jhBzl4
If it turns out there are actually multiple entries and you want to get the id and start values for all of them, you can use a query like this:
JObject obj = JObject.Parse(json);
var items = obj["entry"]
.Children<JObject>()
.Select(o => new
{
ids = o.SelectToken("resource.id").ToObject<List<string>>(),
start = o.SelectToken("resource.start").ToObject<DateTimeOffset>()
})
.ToList();
Demo: https://dotnetfiddle.net/Qe8NB7
I am not sure why you don't deserialize the lot (even if it's minimally populated) since you have to do the inner classes anyway.
Here is how you could bypass some of the classes (1) by digging into the JObjects
Given
public class Reference
{
public string value { get; set; }
}
public class Schedule
{
public Reference reference { get; set; }
}
public class Resource
{
public string resourceType { get; set; }
public List<string> id { get; set; }
public Schedule schedule { get; set; }
public string status { get; set; }
public string start { get; set; }
public string end { get; set; }
}
public class Entry
{
public string url { get; set; }
public Resource resource { get; set; }
}
You could call
var results = JObject.Parse(input)["entry"]
.Select(x => x.ToObject<Entry>());

C# Entity Framework Json deserialize String array issues

I have A Json file Which can be used for deserialize to Entity framework. For simplify we can assume the Json like this
{
"stat": "val0",
"results": [
{
"datasets": [
"val1",
"val2"
],
"head": "val3"
},
{
"datasets": [
"val4",
"val5"
],
"head": "val6"
}
]
}
And my Entity Classes like
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public int stat { get; set; }
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _strings { get; set; }
public List<string> Strings
{
get { return _strings; }
set { _strings = value; }
}
[Required]
public string datasets
{
get { return String.Join(",", _strings); }
set { _strings = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
I know Entity Framework does not support primitive types and I know problem causes from my datasets fields. that I found this way to solve String array deserialize issue here. I have tried
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<RootObject>(json);
But I got
System.InvalidOperationException
Then I have decided to use Newtonsoft
URL = "http://...";//Restful webservice address
WebClient client = new WebClient();
String JSON= client.DownloadString(URL);
var dsobj = JsonConvert.DeserializeObject<Root>(json);
Then I got this error
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: [. Path 'results[0].senses[0].definition', line 1, position...
I found this but I cant figure it out.
How can Fix these isseus. Any help appreciated.
Your json consist of two unwanted commas, try removing those
try
[Serializable]
public class Root
{
[Key]
public int Id { get; set; }
public string stat { get; set; } // changed to a string
public List<Result> results { get; set; }
}
[Serializable]
public class Result
{
[Key]
public int Id { get; set; }
public List<String> _dataSets { get; set; }
public List<string> dataSets // the JSON array will deserialize into this property
{
get { return _dataSets; }
set { _dataSets = value; }
}
[Required]
public string DatasetsAsString
{
get { return String.Join(",", _dataSets); }
set { _dataSets = value.Split(',').ToList(); }
}
public string head{ get; set; }
public virtual root { get; set; }
}
Edit: stat property has to be a string too.

Cast Deserialised Object to Another Type

I'm calling an API that returns a standard response structure for all actions. I deserialise the response using Newtonsoft.Json and JsonConvert.DeserializeObject<Response>
public class Response
{
public int Code { get; set; }
public string Message { get; set; }
public object Result { get; set; }
public DateTime ResponseDateTime { get; set; }
}
The Result object changes depending on the request action that has been requested and if there was an error or not (Code + Message describe errors). I know the structure of the Result objects and have created classes for each Result I require.
How do i go about casting the Result object to a typed variable such as a Detail[]? I know i can serialize the Result object and deserialise it again but surely there is a more elegant solution.
public class Detail
{
public int Id { get; set; }
public string DetailOne { get; set; }
public string DetailTwo { get; set; }
}
If you always know what kind of Result you'll get, I'd suggest making Response generic:
public class Response<TResult>
{
public int Code { get; set; }
public string Message { get; set; }
public T Result { get; set; }
public DateTime ResponseDateTime { get; set; }
}
Then you can just deserialize to a Response<Detail[]> and Json.NET should handle everything fine.
This is assuming that if there's an error, you won't end up with a Result that could be problematic. Alternatively, you could still deserialize to a Response class, but with a JToken property type:
public class Response
{
public int Code { get; set; }
public string Message { get; set; }
public JToken Result { get; set; }
public DateTime ResponseDateTime { get; set; }
}
Then use:
Response response = JsonConvert.DeserializeObject<Response>(json);
if (response.Code == 200) // Or whatever
{
Detail[] details = response.Result.ToObject<Detail[]>();
}
You could potentially wrap that into a generic method. I'd at least try the generic response class first though.
If you change your object model to this:
public class Response<T>
{
public int Code { get; set; }
public string Message { get; set; }
public T Result { get; set; }
public DateTime ResponseDateTime { get; set; }
}
public class Detail
{
public int Id { get; set; }
public string DetailOne { get; set; }
public string DetailTwo { get; set; }
}
Then you can do this:
var original = new Response<Detail>()
{
Code = 42,
Message = "OK",
Result = new Detail()
{
Id = 1701,
DetailOne = "Don't",
DetailTwo = "Panic",
},
ResponseDateTime = DateTime.Now,
};
var json = JsonConvert.SerializeObject(original, Newtonsoft.Json.Formatting.Indented);
var response = JsonConvert.DeserializeObject<Response<Newtonsoft.Json.Linq.JToken>>(json);
if (response.Code == 42)
{
Detail detail = response.Result.ToObject<Detail>();
/* Do something with `Detail`. */
}
That seems a fairly nice way to get to the underlying Detail object.
The key technique is to serialize a Response<Detail> and deserialize as a Response<JToken>. Simple.
Of course, if you really wanted to create a Response<Detail> instance you could make a fairly straightforward way of converting from Response<JToken> to Response<Detail> by doing a straight field-to-field mapping.
Here is the Response<Detail[]> version of the code:
var original = new Response<Detail[]>()
{
Code = 42,
Message = "OK",
Result = new Detail[]
{
new Detail()
{
Id = 1701,
DetailOne = "Don't",
DetailTwo = "Panic",
},
new Detail()
{
Id = 360,
DetailOne = "Microsoft",
DetailTwo = "Xbox",
}
},
ResponseDateTime = DateTime.Now,
};
var json = JsonConvert.SerializeObject(original, Newtonsoft.Json.Formatting.Indented);
var response = JsonConvert.DeserializeObject<Response<Newtonsoft.Json.Linq.JToken>>(json);
if (response.Code == 42)
{
Detail[] detail = response.Result.ToObject<Detail[]>();
/* Do something with `Detail[]`. */
}

Invalid class property declaration for json mapping

I have below json received from mailgun API.
{
"items": [{
"delivery-status": {
"message": null,
"code": 605,
"description": "Not delivering to previously bounced address",
"session-seconds": 0
},
"event": "failed",
"log-level": "error",
"recipient": "test#test.com"
},
{
//some other properties of above types
}]
}
Now I was trying to create a class structure for above json to auto-map the properties after deserializing.
public class test
{
public List<Item> items { get; set; }
}
public class Item
{
public string recipient { get; set; }
public string #event { get; set; }
public DeliveryStatus delivery_status { get; set; }
}
public class DeliveryStatus
{
public string description { get; set; }
}
This is how I deserialize and try to map the properties.
var resp = client.Execute(request);
var json = new JavaScriptSerializer();
var content = json.Deserialize<Dictionary<string, object>>(resp.Content);
test testContent = (test)json.Deserialize(resp.Content, typeof(test));
var eventType = testContent.items[0].#event;
var desc = testContent.items[0].delivery_status.description; //stays null
Now in the above class Item, recipient and #event gets mapped properly and since it was a keyword I was suppose to use preceding # character and it works well. But the delivery-status property from json, does not get mapped with delevery_status property in class DeliveryStatus. I have tried creating it as deliveryStatus or #deliver-status. The earlier on doesn't map again and the later one throws compile time exception. Is there anyway these things can be handled, like declaring a property with - in between? I cannot change response json as it is not getting generated from my end. Hoping for some help.
Update
Changed the class as below referring this answer, but did not help. Its null again.
public class Item
{
public string #event { get; set; }
[JsonProperty(PropertyName = "delivery-status")]
public DeliveryStatus deliveryStatus { get; set; }
}
I am not sure what the issue is at your end, but at least it works if you use this code. Make sure to include a recent version of Newtonsoft.Json in your project and you should be fine.
public class DeliveryStatus
{
public object message { get; set; }
public int code { get; set; }
public string description { get; set; }
[JsonProperty("session-seconds")]
public int session_seconds { get; set; }
}
public class Item
{
[JsonProperty("delivery-status")]
public DeliveryStatus delivery_status { get; set; }
public string #event { get; set; }
[JsonProperty("log-level")]
public string log_level { get; set; }
public string recipient { get; set; }
}
public class RootObject
{
public List<Item> items { get; set; }
}
public static void Main(string[] args)
{
string json = #"{
""items"": [{
""delivery-status"": {
""message"": null,
""code"": 605,
""description"": ""Not delivering to previously bounced address"",
""session-seconds"": 0
},
""event"": ""failed"",
""log-level"": ""error"",
""recipient"": ""test#test.com""
}]
}";
RootObject r = JsonConvert.DeserializeObject<RootObject>(json);
}

How do I deserialize a JSON array using Newtonsoft.Json

[
{
"receiver_tax_id":"1002",
"total":"6949,15",
"receiver_company_name":"Das Company",
"receiver_email":"info#another.com",
"status":0
},
{
"receiver_tax_id":"1001",
"total":"39222,49",
"receiver_company_name":"SAD company",
"receiver_email":"info#mail.com",
"status":1
}
]
Hi, this is my Json data, but I can't deserialize it.
I want to check only "status" value. (first object "status" 0, second object "status" 1).
Example definition:
public class Example
{
[JsonProperty("receiver_tax_id")]
public string receiver_tax_id { get; set; }
[JsonProperty("total")]
public string total { get; set; }
[JsonProperty("receiver_company_name")]
public string receiver_company_name { get; set; }
[JsonProperty("receiver_email")]
public string receiver_email { get; set; }
[JsonProperty("status")]
public int status { get; set; }
}
Deserialization code:
var des = (Example)JsonConvert.DeserializeObject(responseString, typeof(Example));
Console.WriteLine(des.status[0].ToString());
Try this code:
public class Receiver
{
public string receiver_tax_id { get; set;}
public string total { get; set;}
public string receiver_company_name { get; set;}
public int status { get; set;}
}
And deserialize looks like follows:
var result = JsonConvert.DeserializeObject<List<Receiver>>(responseString);
var status = result[0].status;
If you only care about checking status you can use the dynamic type of .NET (https://msdn.microsoft.com/en-us/library/dd264741.aspx)
dynamic deserialized = JObject.Parse(responseString);
int status1 = deserialized[0].status;
int status2 = deserialized[1].status;
//
// do whatever
This way you don't even need the Example class.
From your code and JSON sampels it seems the problem is you're actually deserializing a List<Example> rather than a single Example.
I would do two things:
Make your class follow .NET naming conventions, as you already prefixed them with the proper JsonProperty attributes:
public class Example
{
[JsonProperty("receiver_tax_id")]
public string ReceiverTaxId { get; set; }
[JsonProperty("total")]
public string Total { get; set; }
[JsonProperty("receiver_company_name")]
public string ReceiverCompanyName { get; set; }
[JsonProperty("receiver_email")]
public string ReceiverEmail { get; set; }
[JsonProperty("status")]
public int Status{ get; set; }
}
Deserialize a List<Example> using the generic JsonConvert.DeserializeObject<T> overload instead of the non-generic version you're currently using:
var des = JsonConvert.DeserializeObject<List<Example>>(responseString);
Console.WriteLine(des[0].Status);
You're trying to deserialize an array into an Example object. Try doing it to a List instead:
var des = JsonConvert.DeserializeObject(responseString, typeof(List<Example>)) as List<Example>;

Categories

Resources