JSON Error when attempting to consume data from an external API.

Aaron.soggi92

Member
Joined
Oct 22, 2021
Messages
19
Programming Experience
Beginner
System.AggregateException
HResult=0x80131500
Message=One or more errors occurred. (Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[OpenAQAirQuality.Models.City]' 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<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 'meta', line 1, position 8.)
Source=System.Private.CoreLib
StackTrace:
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at OpenAQAirQuality.ClientConnectionHandler.ClientConnectionHandler.GetCityData() in C:\Users\Aaron\source\repos\OpenAQAirQuality\OpenAQAirQuality\Services\ClientConnectionHandler.cs:line 29
at OpenAQAirQuality.Controllers.airQualityController.getCity() in C:\Users\Aaron\source\repos\OpenAQAirQuality\OpenAQAirQuality\Controllers\airQualityController.cs:line 19
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

This exception was originally thrown at this call stack:
[External Code]

Inner Exception 1:
JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[OpenAQAirQuality.Models.City]' 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<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 'meta', line 1, position 8.



ClientConnectionHandler code

C#:
        public ActionResult GetCityData()
        {
            IEnumerable<City> city = null;
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("https://docs.openaq.org/");
                var responseTask = client.GetAsync("v2/cities");
                responseTask.Wait();

                var result = responseTask.Result;
                if (result.IsSuccessStatusCode)
                {
                  
                    var readData = result.Content.ReadAsAsync<IList<City>>();
                    readData.Wait(); // this is where the error is occuring
                    city = readData.Result;

                }
                else
                {
                    city = Enumerable.Empty<City>();
                    ModelStateDictionary modelState = new ModelStateDictionary();
                    modelState.AddModelError(string.Empty, "Server error has occured");
                    

                }
            }
            
            return (ActionResult)city;


Class for City model:

C#:
public class City
    {
        [JsonProperty]
        public string country { get; set; }
        [JsonProperty]
        public string city { get; set; }
        [JsonProperty]
        public int count { get; set; }
        [JsonProperty]
        public int locations { get; set; }
        [JsonProperty]
        public DateTime firstUpdated { get; set; }
        [JsonProperty]
        public DateTime lastUpdated { get; set; }
        [JsonProperty]
        public string[] parameters { get; set; }

    }

Does anyone know why this might be occurring and how it can be fixed? Thankyou
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,711
Location
Chesapeake, VA
Programming Experience
10+
Read the error closely:
C#:
Inner Exception 1:
JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[OpenAQAirQuality.Models.City]' 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<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 'meta', line 1, position 8.

If you look at the response from the REST call, it looks something like:
C#:
{
  "meta":{
    "name":"openaq-api",
    "license":"CC BY 4.0d",
    "website":"https://u50g7n0cbj.execute-api.us-east-1.amazonaws.com/",
    "page":1,
    "limit":100,
    "found":3151
  },
  "results":[
    {
      "country":"US",
      "city":"007",
      "count":22605,
      "locations":6,
      "firstUpdated":"2018-08-09T09:00:00+00:00",
      "lastUpdated":"2022-05-09T13:00:00+00:00",
      "parameters":["pm25"]
    },
    {
      "country":"US",
      "city":"019",
:

You need to parse just the results part into an IList<City>, but currently you are passing in more than that to ReadAsAsync().
 

Aaron.soggi92

Member
Joined
Oct 22, 2021
Messages
19
Programming Experience
Beginner
@Skydiver Really appreciate the prompt response. I've tried to figure out how to do that exactly and still haven't had any luck. Would you mind explaining how i would go about extracting just the results and give an example?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,711
Location
Chesapeake, VA
Programming Experience
10+
Your best bet would be to just define an outer class that contains that results list that you are expecting.

The alternative is to just use Newtonsoft's JSON.NET LINQ support to get to just the results list part, but at that point you might as well use JSON.NET all the way instead of using the Microsoft JSON serializer to parse the list.
 

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,471
Location
Norway
Programming Experience
10+
Top Bottom