C
C#3mo ago
Xraxis

unable to parse Json data pulled from API as a list of strings

I am trying to take some data from an API called Scryfall API and assign it to a List variable. I am very new to this and have as of yet been unsuccessful in saving the URLs to the images in the API as a list of strings that I am going to use later. If anyone has any ideas I really could use the help. The main line of code I am using is:
List<string> imageUrls = jsonResponse["data"].Select(item => item["png"].ToString()).ToList();
List<string> imageUrls = jsonResponse["data"].Select(item => item["png"].ToString()).ToList();
Here is the rest of my retrieval code:
using Newtonsoft.Json.Linq;

namespace MBT.API
{
public class APIHelper
{

// Limit the number of requests in a time period
private readonly int MaxRequestsPerSecond = 15;
private readonly TimeSpan RequestInterval = TimeSpan.FromSeconds(1);
private DateTime lastRequestTime = DateTime.MinValue;
private int requestCount = 0;

private static HttpClient apiClient = new()
{

BaseAddress = new Uri("https://api.scryfall.com/cards/search?q=type%3Aland+-t%3Acreature+-t%3Aartifact+-t%3Aenchantment+-t%3Aplaneswalker+-t%3Asorcery+-t%3Ainstant"),

};

public async Task<List<string>> GetLandImages()
{

var url = "https://api.scryfall.com/cards/search?q=type%3Aland+-t%3Acreature+-t%3Aartifact+-t%3Aenchantment+-t%3Aplaneswalker+-t%3Asorcery+-t%3Ainstant";
var response = await apiClient.GetAsync(url);
bool requestCountStatus = CanMakeRequest();

using (apiClient = new HttpClient(new HttpClientHandler
{

SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13

}))
{

if (response.IsSuccessStatusCode && requestCountStatus)
{

string responseBody = await response.Content.ReadAsStringAsync();
JObject jsonResponse = JObject.Parse(responseBody);

if(jsonResponse["data"] != null && jsonResponse["data"].HasValues)
{

List<string> imageUrls = jsonResponse["data"].Select(item => item["png"].ToString()).ToList(); //Broken. Need to fix.
return imageUrls;

}

return new List<string>();

}

return new List<string>();

}
}

bool CanMakeRequest()
{
DateTime now = DateTime.Now;

if (now - lastRequestTime > RequestInterval)
{
lastRequestTime = now;
requestCount = 0;
}

if (requestCount < MaxRequestsPerSecond)
{
requestCount++;
return true;
}

return false;
}
}
}
using Newtonsoft.Json.Linq;

namespace MBT.API
{
public class APIHelper
{

// Limit the number of requests in a time period
private readonly int MaxRequestsPerSecond = 15;
private readonly TimeSpan RequestInterval = TimeSpan.FromSeconds(1);
private DateTime lastRequestTime = DateTime.MinValue;
private int requestCount = 0;

private static HttpClient apiClient = new()
{

BaseAddress = new Uri("https://api.scryfall.com/cards/search?q=type%3Aland+-t%3Acreature+-t%3Aartifact+-t%3Aenchantment+-t%3Aplaneswalker+-t%3Asorcery+-t%3Ainstant"),

};

public async Task<List<string>> GetLandImages()
{

var url = "https://api.scryfall.com/cards/search?q=type%3Aland+-t%3Acreature+-t%3Aartifact+-t%3Aenchantment+-t%3Aplaneswalker+-t%3Asorcery+-t%3Ainstant";
var response = await apiClient.GetAsync(url);
bool requestCountStatus = CanMakeRequest();

using (apiClient = new HttpClient(new HttpClientHandler
{

SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13

}))
{

if (response.IsSuccessStatusCode && requestCountStatus)
{

string responseBody = await response.Content.ReadAsStringAsync();
JObject jsonResponse = JObject.Parse(responseBody);

if(jsonResponse["data"] != null && jsonResponse["data"].HasValues)
{

List<string> imageUrls = jsonResponse["data"].Select(item => item["png"].ToString()).ToList(); //Broken. Need to fix.
return imageUrls;

}

return new List<string>();

}

return new List<string>();

}
}

bool CanMakeRequest()
{
DateTime now = DateTime.Now;

if (now - lastRequestTime > RequestInterval)
{
lastRequestTime = now;
requestCount = 0;
}

if (requestCount < MaxRequestsPerSecond)
{
requestCount++;
return true;
}

return false;
}
}
}
and this is the properties for the json retrieval itself:
using Newtonsoft.Json;

namespace MBT.API
{

public partial class ScryfallApiResponse
{

[JsonProperty("data")]
public List<CardFace> Cards { get; set; }

}

public partial class CardFace
{
[JsonProperty("image_uris")]
public ImageUris ImageUrls { get; set; }
}

public partial class ImageUris
{

[JsonProperty("png")]
public Uri Png { get; set; }

}
}
using Newtonsoft.Json;

namespace MBT.API
{

public partial class ScryfallApiResponse
{

[JsonProperty("data")]
public List<CardFace> Cards { get; set; }

}

public partial class CardFace
{
[JsonProperty("image_uris")]
public ImageUris ImageUrls { get; set; }
}

public partial class ImageUris
{

[JsonProperty("png")]
public Uri Png { get; set; }

}
}
Thank you for any help in advance.
15 Replies
Pobiega
Pobiega3mo ago
I have some questions. You're using Newtonsoft.Json, why is that? Why not System.Text.Json, the default built-in json package that is both more performant and more idiomatic in its usage? You're doing JObject.Parse, aka throwing types to the wind. That makes little sense in the first place, but even less when I see you made a response model class. Please, use await httpClient.GetAsJsonAsync<YourResponseHere>(url) and your life will be so much easier. could you please paste an example of the API response as json?
Xraxis
Xraxis3mo ago
Here is a part of the the response data. Also, I changed the code to not use parse since the post. This is the new code i am using:
JObject jsonResponse = JsonConvert.DeserializeObject<JObject>(responseBody);
JObject jsonResponse = JsonConvert.DeserializeObject<JObject>(responseBody);
Pobiega
Pobiega3mo ago
Okay great. Thats a really big payload, what data are you interested in? just the imageUri under cardFace?
Xraxis
Xraxis3mo ago
actually I want the png url from the Json. which happens to be nested in imageUri.
Pobiega
Pobiega3mo ago
and thats it? you only want the png url? its worth mentioning that the scryfall api is paginated, ie that you cant get all data in one call with this many results is it fine to just get the first page, or do you want to actually get all the results, which will involve sending multiple requests
Xraxis
Xraxis3mo ago
for now the first page is fine. I just need get the png data to try and create the rest of my application.
Pobiega
Pobiega3mo ago
okay step 1 is to make a model that exactly matches the structure of the response. You've pretty much done that already with your ScryfallApiResponse class, but we need to change the newtonsoft attributes for STJ ones
Xraxis
Xraxis3mo ago
okay.
Pobiega
Pobiega3mo ago
JsonProperty becomes JsonPropertyName thats actually it, the rest is the same for modelling
Xraxis
Xraxis3mo ago
okay, I changed the using and added Name to the end of each property.
Pobiega
Pobiega3mo ago
okay great, now lets clean up the code a bit you're setting the url twice, so I'd recommend just removing the BaseAddress stuff entirely
Xraxis
Xraxis3mo ago
I will be back in a bit getting kicked out of the classroom I am in. Thanks for the help btw.
Pobiega
Pobiega3mo ago
okay. there is some pretty weird stuff going on here tbh you make the request (fine), but then after you made the request, you have a CanMakeRequest() method that seems to do some timing stuff but the request has already been made then you re-make your static HttpClient (bad), with a using statement (extremely bad), and set the TLS protocols manually? why? after that, you check if the request succeeded and proceed to do the nasty manual json stuff Thats all way too much stuff. All you need is to... * Make a static HttpClient. Don't touch it further. * Make the request with await client.GetFromJsonAsync<ScryfallApiResponse>(url); * check if the response was null, return empty list * use Select to map the response list into a list of strings. * return the result its about 6 lines of code tbh
Xraxis
Xraxis3mo ago
Okay, I'll give this a shot. Thank you.
Pobiega
Pobiega3mo ago
let me know if you get stuck
Want results from more Discord servers?
Add your server
More Posts