Blazor: Cannot deserialize the current JSON object - c#

I am creating an app in Blazor, upon getting a list of data displayed(Index View) I am running into the following error:
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.
I have heard that Blazor does not support HttpClient for Injection. So I wrote a custom HttpClient to do the same job, however I ran into an issue and unable to understand why I am getting it.
Below is a copy of my entity section:
public class Tournament
{
[Key]
public int TournamentID { get; set; }
public string TournamentName { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
A copy of my code section in razor page:
#code {
Tournament[] tournaments;
string baseUrl;
protected override async Task OnInitializedAsync()
{
baseUrl = AppSettingsService.GetBaseUrl();
tournaments = await Http.GetJsonAsync<Tournament[]>(baseUrl + "api/tournaments/gettournaments");
}
}
And a copy of my custom http client class:
public async Task<T> GetJsonAsync<T>(string requestUri)
{
HttpClient httpClient = new HttpClient();
var httpContent = await httpClient.GetAsync(requestUri);
string jsonContent = httpContent.Content.ReadAsStringAsync().Result;
T obj = JsonConvert.DeserializeObject<T>(jsonContent);
httpContent.Dispose();
httpClient.Dispose();
return obj;
}
Can anyone help me further in this regard?
Thanks.

Related

Why can't I deserialize string Into a model?

I'm using a HTTP client to get a string and picking out my json from that and converting back to a string to deserialize it into a List of "Spots" but can't get it to to work
I've tried changing the DeserializeObject type to every mix of "List, IList, HardwareUpdateSpot, HardWareModel" and still it didn't work
public async Task<IList<HardwareUpdateSpot>> UpdateSpotHTTP()
{
var client = new HttpClient();
var response = await client.GetAsync(
"https://io.adafruit.com/api/v2/Corey673/feeds/673d855c-9f66-4e49-8b2c-737e829d880c");
var responseHTTP = response.Content.ReadAsStringAsync();
var j = JObject.Parse(responseHTTP.Result);
var b = j.GetValue("last_value");
var h = b.ToString();
var dataObjects = JsonConvert.DeserializeObject<IList<HardwareUpdateSpot>>(h);
return null;
}
public record HardWareModel
{
public int SpotId { get; set; }
public string Occupied { get; set; }
}
public class HardwareUpdateSpot
{
public IList<HardWareModel> Spots { get; set; }
public HardwareUpdateSpot(IList<HardWareModel> spots)
{
Spots = spots;
}
}
While trying to reproduce your problem I have examined the returned value from the API call. This is the json returned:
{"Spot":[
{"SpotId":"1","Occupied":"false",},
{"SpotId":"2","Occupied":"false",},
{"SpotId":"3","Occupied":"false",},
{"SpotId":"4","Occupied":"false"}
]}
So, it easy to see that the returned json requires a root object with a public Spot property (not Spots) and this property should be a collection.
Instead the code above expects a json that has at the root level a collection of HardwareUpdateSpot and of course it cannot work.
To fix the problem you need to change the deserialization to:
JsonConvert.DeserializeObject<HardwareUpdateSpot>(h);
Now, you need to make some changes to the HardwareUpdateSpot class to make it compatible with the json.
First you need to add a parameterless constructor required by jsonconvert, then you need to fix the difference between the name for the property (Spots) and the name returned (Spot).
So you can change the property name to match the json or add the attribute that make Spots=Spot
[JsonProperty("Spot")]
public IList<HardWareModel> Spots { get; set; }

Deserilizing the json to list of an object throwing error. Cannot deserialize the current JSON object (e.g. {"name":"value"})

I am trying to deserialize the Json to List object of Student which conister of studentName and studentId. I do get the jsonResponse with around 200 students but when I get to deserialize I got the below error. I did reserch for this error and the fix for the issue is similar to the code that I have so I am not sure what is wrong.
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyApp.Models.Student]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
public static async Task<List<Student>> GetUserInfo()
{
var token = await AccessToken.GetGraphAccessToken();
// Construct the query
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Globals.MicrosoftGraphUsersApi);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
// Ensure a successful response
HttpResponseMessage response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
// Populate the data store with the first page of groups
string jsonResponse = await response.Content.ReadAsStringAsync();
var students = JsonConvert.DeserializeObject<List<Student>>(jsonResponse);
return students;
}
Below is the JSON response from Microsoft Graph Api
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(studentName,studentId)",
"value": [
{"studentName":"Radha,NoMore","studentId":"420"},
{"studentName":"Victoria, TooMuch","studentId":"302"}
]
}
C# student Class:
public class Student
{
public string studentName { get; set; }
public string studentId { get; set; }
}
The JSON response contains a value: property, and that property contains the students as array data. Therefore you'll need to make an additional class that has a List<Student> value property, deserialize to that class, and then you can use the List of Students that is in the value property, as follows:
var listHolder = JsonConvert.DeserializeObject<StudentListHolder>(jsonResponse);
var list = listHolder.value;
foreach (var student in list)
{
Console.WriteLine(student.studentId + " -> " + student.studentName);
}
This is the additional class:
public class StudentListHolder // pick any name that makes sense to you
{
public List<Student> value { get; set; }
}
Working demo (.NET Fiddle): https://dotnetfiddle.net/Lit6Er

Converting a JSON into own type results in null objects

from my rest call, I am receiving this JSON:
{
"livemode": true,
"error": {
"type": "unauthorized",
"message": "You did not provide a valid API key."
}
}
I need to fetch type and message into my type:
public class TestObject
{
string type { get; set; }
string message { get; set; }
}
But this returns null objects:
HttpClient client = new HttpClient();
Uri uri = new Uri("https://api.onlinebetaalplatform.nl/v1");
HttpResponseMessage response = await client.GetAsync(uri);
string content = await response.Content.ReadAsStringAsync();
JObject json = JObject.Parse(content);
TestObject album = json.ToObject<TestObject>();
1.) I understand that the type and message attributes are "nested". How do I access them?
2.) Even if I call my type livemode and error, the objects still return null.
Can you help me out a little?
Thank you :)
There seems to be one set of curly brackets to many. I am pretty sure that the api you are querying is not returning the first and the last curly bracket. Continue on after that has been taken care of.
In order to fetch the data, add these class definitions
public class Error
{
public string type { get; set; }
public string message { get; set; }
}
public class Root
{
public bool livemode { get; set; }
public Error error { get; set; }
}
and change
TestObject album = json.ToObject<TestObject>();
To
Root album = json.ToObject<Root>();
As some of the comments to your question mentioned, you are currently trying to convert the JSON string to the nested Error object instead of the root object, where the Error object is located.
In the future, there are tools that can generate C# classes from JSON. I used https://json2csharp.com/ this time around to do so.
EDIT:
I just found out that Visual Studio actually has an in-built JSON to Class feature!

C# .net http | How to get objects layers deep from API

I learned how to get information from an API using the microsoft docs, the microsoft docs don't show how to get nested/layers deep objects. The only video I found that showed how to do it did it something like this. However I can't get it to work, receiving only an error stating this:
"Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Dashboard.Weather' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly."
Any help is appreciated, i'm trying to get the "Weather.description", here's my sample code:
public class Weather
{
public string Description{ get; set; }
}
public class Product
{
public Weather Weather { get; set; }
}
public static class ApiHelper
{
static string city_id = "CITY_ID";
static string api_key = "API_KEY";
public static HttpClient client = new HttpClient();
public static void InitializeClient()
{
client.BaseAddress = new Uri($"http://api.openweathermap.org/data/2.5/weather?id={city_id}&APPID={api_key}");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public static async Task<Weather> GetProductAsync()
{
Product product = null;
HttpResponseMessage response = await client.GetAsync("");
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product.Weather;
}
}
async void SetLabelText()
{
var weather = await ApiHelper.GetProductAsync();
descriptionLabel.Text = $"Description: {weather.Description}";
}
The response from the API is formatted as follows
{"coord":{"lon":-89.59,"lat":41.56},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":279.27,"feels_like":275.4,"temp_min":278.15,"temp_max":280.37,"pressure":1027,"humidity":74},"visibility":16093,"wind":{"speed":3.1,"deg":200},"clouds":{"all":1},"dt":1576951484,"sys":{"type":1,"id":3561,"country":"US","sunrise":1576934493,"sunset":1576967469},"timezone":-21600,"id":4915397,"name":"Walnut","cod":200}
Your Product model does not correctly align with the json you are receiving.
The json you've post has weather as a list, but Product assumes it will just be an object. The json parser, then, correctly fails when seeing it is an array in the actual json instead of a JSON object.
The fix should be simple; Product.Weather should be of type List<Weather> (or IEnumerable<Weather> or Weather[], whichever fits your needs).

Deserialize Web Api OData response

I have an Entity Framework object returned by OData V4 controller.
I return an IQueryable and if I call the OData endpoint without any OData clause I can succesfully do this:
var content = response.Content.ReadAsAsync<IQueryable<Person>>();
And the response in JSON is the following:
{
"#odata.context":"http://xxx:8082/odata/$metadata#Persons","value":[
{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5",
"Active":true,
"Alert":"Some alerts for the Person",
"Comments":"Some comments for the Person"
}
]
}
But as soon as I start to play with OData, for example by using $expand on a Complex property I get the following exception:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Linq.IQueryable`1[xxx.Person]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
And the response is the following:
{
"#odata.context":"http://aqavnext01:8082/odata/$metadata#Persons","value":[
{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5",
"Active":true,
"Alert":"Some alerts for the Person",
"Comments":"Some comments for the Person",
"Party":{
"Id":"291b9f1c-2587-4a35-993e-00033a81f6d5"
}
}
]
}
And I am deserializing using the same object returned by my Web Api so I don't understand why it fails.
Same issue when I apply $select.
Try deserialize the content like this:
var content = response.Content.ReadAsAsync<ODataResponse<Person>>();
Where ODataResponse is:
internal class ODataResponse<T>
{
public T[] Value { get; set; }
}
If you need access to the #odata.xxx fields in the response JSON (such as implementing a loop for paged results), the following is my implementation which expands on the solution from andygjp.
I'm using RestSharp as my HTTP client and Json.NET for (de)serialization. Steps as follows.
Implement a custom IRestSerializer that uses Json.NET to replace the default RestSharp serializer. Below example implementation:
public class JsonNetSerializer : IRestSerializer
{
private readonly JsonSerializerSettings _settings;
public JsonNetSerializer()
{
_settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
_settings.Converters.Add(new StringEnumConverter());
}
public string Serialize(Parameter parameter) => JsonConvert.SerializeObject(parameter.Value, Formatting.None, _settings);
public string Serialize(object obj) => JsonConvert.SerializeObject(obj, Formatting.None, _settings);
public T Deserialize<T>(IRestResponse response) => JsonConvert.DeserializeObject<T>(response.Content);
public string[] SupportedContentTypes => new string[] { "application/json", "text/json", "text/x-json", "text/javascript", "*+json" };
public DataFormat DataFormat => DataFormat.Json;
public string ContentType { get; set; } = "application/json";
}
Next, define a class that will represent your OData responses. My expanded response class - which includes the #odata.nextLink field - looks as follows.
private class ODataResponse<T>
{
public T[] Value { get; set; }
[JsonProperty("#odata.nextLink")]
public string NextLink { get; set; }
}
Finally, I create an instance of RestClient, setting up the custom serializer previously created:
var client = new RestClient("https://base.url.here/")
.UseSerializer(() => new JsonNetSerializer());
Now when I execute my request, the data in the response object object also contains my OData values.
var response = await client.ExecuteAsync<T>(request);
var nextLink = response.Data.NextLink;
I'm sure this can be done using the standard HttpClient instead of RestSharp, as the real work is done by the serializer. This was just the example implementation I had on hand.

Categories