'ASP.NET MVC Core Consuming Web API with Arrays?

I am having difficulty and although I thoroughly (I think) researched the web, including here at SO, I cannot seem to find what I am looking for -- or I am being obtuse. My environment is .NET6, ASP.NET Core MVC.

The error I am encountering after successfully grabbing the Json from my API is:

JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ConsumeWebApi.Models.Weather+Root]' 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 'region', line 1, position 10.

That seems pretty straightforward to me, so I examined the returned string.

{
    "region": "My Town, ST",
    "currentConditions": {
        "dayhour": "Tuesday 9:00 AM",
        "temp": {
            "c": 14,
            "f": 58
        },
        "precip": "15%",
        "humidity": "72%",
        "wind": {
            "km": 3,
            "mile": 2
        },
        "iconURL": "https://ssl.gstatic.com/onebox/weather/64/sunny.png",
        "comment": "Sunny"
    },
    "next_days": [
        {
            "day": "Tuesday",
            "comment": "Scattered showers",
            "max_temp": {
                "c": 24,
                "f": 75
            },
            "min_temp": {
                "c": 14,
                "f": 58
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/rain_s_cloudy.png"
        },
        {
            "day": "Wednesday",
            "comment": "Scattered showers",
            "max_temp": {
                "c": 17,
                "f": 62
            },
            "min_temp": {
                "c": 9,
                "f": 49
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/rain_s_cloudy.png"
        },
        {
            "day": "Thursday",
            "comment": "Mostly cloudy",
            "max_temp": {
                "c": 22,
                "f": 71
            },
            "min_temp": {
                "c": 12,
                "f": 54
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/partly_cloudy.png"
        },
        {
            "day": "Friday",
            "comment": "Rain",
            "max_temp": {
                "c": 17,
                "f": 62
            },
            "min_temp": {
                "c": 12,
                "f": 53
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/rain.png"
        },
        {
            "day": "Saturday",
            "comment": "Showers",
            "max_temp": {
                "c": 16,
                "f": 60
            },
            "min_temp": {
                "c": 8,
                "f": 46
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/rain_light.png"
        },
        {
            "day": "Sunday",
            "comment": "Partly cloudy",
            "max_temp": {
                "c": 19,
                "f": 66
            },
            "min_temp": {
                "c": 7,
                "f": 45
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/partly_cloudy.png"
        },
        {
            "day": "Monday",
            "comment": "Partly cloudy",
            "max_temp": {
                "c": 22,
                "f": 72
            },
            "min_temp": {
                "c": 9,
                "f": 48
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/partly_cloudy.png"
        },
        {
            "day": "Tuesday",
            "comment": "Mostly sunny",
            "max_temp": {
                "c": 26,
                "f": 79
            },
            "min_temp": {
                "c": 13,
                "f": 55
            },
            "iconURL": "https://ssl.gstatic.com/onebox/weather/48/partly_cloudy.png"
        }
    ],
    "contact_author": {
        "email": "[email protected]",
        "auth_note": "Mail me for feature requests, improvement, bug, help, ect... Please tell me if you want me to provide any other free easy-to-use API services"
    },
    "data_source": "https://www.google.com/search?lr=lang_en&q=weather+in+mycity+state"
}

The only array I see is for "next_days," but as you can see in my data model below, I accounted for that. My model, which was created by "Paste Json as Classes" feature in Visual Studio, is:

namespace ConsumeWebApi.Models
{
    public class Weather
    {

        public class Root
        {
            public string? Region { get; set; }
            public Currentconditions? CurrentConditions { get; set; }
            public Next_Days[]? Next_days { get; set; }
            public Contact_Author? Contact_author { get; set; }
            public string? Data_source { get; set; }
        }

        public class Currentconditions
        {
            public string? Dayhour { get; set; }
            public Temp? Temp { get; set; }
            public string? Precip { get; set; }
            public string? Humidity { get; set; }
            public Wind? Wind { get; set; }
            public string? IconURL { get; set; }
            public string? Comment { get; set; }
        }

        public class Temp
        {
            public int? C { get; set; }
            public int? F { get; set; }
        }

        public class Wind
        {
            public int? Km { get; set; }
            public int? Mile { get; set; }
        }

        public class Contact_Author
        {
            public string? Email { get; set; }
            public string? Auth_note { get; set; }
        }

        public class Next_Days
        {
            public string? Day { get; set; }
            public string? Comment { get; set; }
            public Max_Temp? Max_temp { get; set; }
            public Min_Temp? Min_temp { get; set; }
            public string? IconURL { get; set; }
        }

        public class Max_Temp
        {
            public int? C { get; set; }
            public int? F { get; set; }
        }

        public class Min_Temp
        {
            public int? C { get; set; }
            public int? F { get; set; }
        }


    }
}

Let's look at my controller. (This is where I suspect I'm not quite getting it. Here or my model.)

        public async Task<IActionResult> Index()
        {
            List<Weather.Root> weatherInfo = new List<Weather.Root>();
            using (var httpClient = new HttpClient())
            {
                using (var response = await httpClient.GetAsync("https://weatherdbi.herokuapp.com/data/weather/mycity+state"))
                {
                    string? apiResponse = await response.Content.ReadAsStringAsync();
                    weatherInfo = JsonConvert.DeserializeObject<List<Weather.Root>>(apiResponse);
                }
            }
            return View(weatherInfo);
        }

Debugging in Visual Studio shows apiResponse to have the Json data I posted above - no extra square brackets [] to be found.

I apologize for the rudimentary question and thank you in advance for your responses. I am new to C#, but not new to web development.



Solution 1:[1]

Root is an object, not a collection. So use just this code

 weatherInfo = JsonConvert.DeserializeObject<Weather.Root>(apiResponse);

Solution 2:[2]

To solve "Cannot deserialize the current JSON array (e.g. [1,2,3])" Follow the steps:

  1. use an online converter that converts JSON to C# (e.g. https://json2csharp.com/).
  2. Paste your JSON in the JSON section and generate C#.
  3. It will generate the code and will provide you with a way to deserialize. e.g. JsonDataService myData = JsonConvert.DeserializeObject(JSON); //JSON parsing

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Serge
Solution 2 Shrembo