2

This is my first time attempting to use APIs with Blazor, and I am struggling to save info from nested JSON objects into C# objects. I have been following these guides: https://learn.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-6.0&pivots=server https://alexb72.medium.com/how-to-make-an-api-call-in-blazor-server-136e4154fca6

My API returns this data:

{
    "page": 1,
    "results": [
        {
            "adult": false,
            "backdrop_path": "/c6OLXfKAk5BKeR6broC8pYiCquX.jpg",
            "genre_ids": [
                18,
                53,
                35
            ],
            "id": 550,
            "original_language": "en",
            "original_title": "Fight Club",
            "overview": "A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground \"fight clubs\" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.",
            "popularity": 100.28,
            "poster_path": "/pB8BM7pdSp6B6Ih7QZ4DrQ3PmJK.jpg",
            "release_date": "1999-10-15",
            "title": "Fight Club",
            "video": false,
            "vote_average": 8.4,
            "vote_count": 25110
        },
        {
            "adult": false,
            "backdrop_path": null,
            "genre_ids": [
                28
            ],
            "id": 347807,
            "original_language": "hi",
            "original_title": "Fight Club: Members Only",
            "overview": "Four friends head off to Bombay and get involved in the mother and father of all gang wars.",
            "popularity": 2.023,
            "poster_path": "/aXFmWfWYCCxQTkCn7K86RvDiMHZ.jpg",
            "release_date": "2006-02-17",
            "title": "Fight Club: Members Only",
            "video": false,
            "vote_average": 3.4,
            "vote_count": 9
        }
    ],
    "total_pages": 2,
    "total_results": 36
}

I have configured an API call that can correctly deserialze the page, total_pages, and total_results headers. If I try to display the results header on my page as a string, I get the error "Cannot get the value of a token type 'StartArray' as a string". I would like to save the results header into a list which I can iterate through on my page, but I am not sure how to handle this StartArray data type.

My C# code for this page (I have also tried List and IEnumerable for the results variable):

@code {
    public class MovieItem
    {
        public int id { get; set; }
        public string title { get; set; }
    }

    public class APIItem
    {
        public int page { get; set; }
        public IList<MovieItem> results { get; set; }
        public int total_pages { get; set; }
        public int total_results { get; set; }
    }

    public APIItem api_item = new APIItem();

    protected override async Task OnInitializedAsync()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "https://api.themoviedb.org/3/search/movie?api_key=HIDDEN&query=fight%20club");
        var client = ClientFactory.CreateClient();
        var response = await client.SendAsync(request);
        if (response.IsSuccessStatusCode)
        {
            await using var responseStream = await response.Content.ReadAsStreamAsync();
            api_item = await JsonSerializer.DeserializeAsync<APIItem>(responseStream);
        }
    }
}

Finally, when I try to display the API results on my page using the following code, I get this error: System.NullReferenceException: 'Object reference not set to an instance of an object'. When I comment out the @foreach section then the rest of the data loads correctly:

    <p>@api_item.page</p>
    <p>@api_item.total_pages</p>
    <p>@api_item.total_results</p>

    @foreach (var movie in api_item.results)
    {
        @movie.id
        @movie.title
    }

I am not sure what to try next from here. If there is a better way to handle this API call in general then I would appreciate any suggestions; I went with this method simply because it's how it was documented on Microsoft's Blazor API guide. Thanks for the help.

2 Answers 2

2

When the first awaited task (var response = await client.SendAsync(request);) inside OnInitializedAsync is reached, Blazor renders the UI. At this point api_item.results is still null. So you need to check if the list is not null before iterating it.

@if (api_item.results != null)
{
    @foreach (var movie in api_item.results)
    {
        @movie.id
        @movie.title
    }
}
else
{
    <p>Loading...</p>
}

The other properties do not produce exception because you have already initialized api_item with public APIItem api_item = new APIItem();.

Another approach would be this:

@if (api_item != null)
{
    <p>@api_item.page</p>    
    <p>@api_item.total_pages</p>    
    <p>@api_item.total_results</p>
    
    @foreach (var movie in api_item.results)
    {
        @movie.id
        @movie.title
    }
}
else
{
    <p>Loading...</p>
}

@code {
    public APIItem api_item = null;

    protected override async Task OnInitializedAsync()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "https://api.themoviedb.org/3/search/movie?api_key=HIDDEN&query=fight%20club");
        var client = ClientFactory.CreateClient();
        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            await using var responseStream = await response.Content.ReadAsStreamAsync();
            api_item = await JsonSerializer.DeserializeAsync<APIItem>(responseStream);
        }
    }
}

You can find this pattern being used in the WeatherForecast page of a Blazor project template.

Razor component lifecycle

Razor component rendering

Sign up to request clarification or add additional context in comments.

Comments

1

I would not recommend to use a ReadAsStream in this case, it is sometimes tricky. Try this

     if (response.IsSuccessStatusCode)
    {
        var json = await response.Content.ReadAsStringAsync();
         api_item = JsonSerializer.Deserialize<APIItem>(json);
    } //check data in a debuger at this point

and fix the class. Dont use any interfaces to deserilize any data.For example for IEnumerable compiler doesn't what class to create - List, Array or something else.

    public class APIItem
    {
       ...
        public List<MovieItem> results { get; set; }
        .....
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.