skip to Main Content

My issue is that I’m trying to get an API endpoint to display but keep running into an error. I’m a bit of a newbie and not at all sure how to construct my code to resolve the issue.

My Error

Error while extracting response for type [class com.jokeapi.jokeDemo.domain.jokeResponse] and content type [application/json;charset=utf-8]] with root cause com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type com.jokeapi.jokeDemo.domain.jokeResponse from Array value (token JsonToken.START_ARRAY)

I’m using Spring Boot and have been following a tutorial for connecting to API endpoints

In my spring boot class looks like this with setters and getters. This works fine for the other API endpoints that return objects in key value pairs

@Getter
@Setter
public class jokeResponse {
    private Long id;
    private String type;
    private String setup;
    private String punchline;
}

As an example I’m able to return the JSON endpoints for objects that are in my jokeResponse class:

{
id: 281,
type: "general",
setup: "When does a joke become a dad joke?",
punchline: "When it becomes apparent."
}

But here is my problem. In my implementation class i have the following with a return method for getForObjects pointing to the jokeResponse class for this endpoint that points to that array. Which is wrong because the class only points to the API endpoint for objects with key value pairs from above.

 @Override
public jokeResponse getTypesList() {
    RestTemplate restTemplate = new RestTemplate();
     return restTemplate.getForObject("https://official-joke-api.appspot.com/types", jokeResponse.class);
}

The JSON endpoint looks like this, an array of strings:

[
 "general",
 "knock-knock",
 "programming",
 "dad"
] 

I need to construct the method so it pulls the array of strings vs the objects and I’m not sure how to construct that.

I’ve tried replacing the class with String[].class in the getForObjects method but that doesn’t resolve it.

2

Answers


  1. Chosen as BEST ANSWER

    With a little trial and error I was able to resolve this by replaceing the class name in the method with the type.

     @Override
        public String getTypesList() {
            RestTemplate restTemplate = new RestTemplate();
            return restTemplate.getForObject("https://official-joke-api.appspot.com/types", String.class);
    }
    

  2. I think you are confusing the endpoints. The /types returns a String[] and the /random returns a Joke.

    Here is a general setup that you can follow. I tested this out locally, and it works great.

    application.properties

    This property controls the base URL for the joke API.

    joke.api.base-url=https://official-joke-api.appspot.com
    

    Application

    Ensure that a RestTemplate bean is created in the Application.

    package org.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.client.RestTemplateBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class Application {
      public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
      }
    
      @Bean
      public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
      }
    }
    

    ApiHelper

    This encapsulates the URL and provides a simple fetchData method.

    package org.example.api;
    
    import java.net.URI;
    import java.net.URISyntaxException;
    import lombok.RequiredArgsConstructor;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import org.springframework.web.client.RestTemplate;
    
    @Component
    @RequiredArgsConstructor  // Injects: restTemplate
    public class ApiHelper {
      private final RestTemplate restTemplate;
    
      @Value("${joke.api.base-url}")
      private String baseUrl;
    
      public <T> T fetchData(String endpoint, Class<T> clazz) {
        try {
          URI uri = new URI(baseUrl).resolve(endpoint);
          return restTemplate.getForObject(uri, clazz);
        } catch (URISyntaxException e) {
          throw new RuntimeException("Invalid URI syntax: " + baseUrl + endpoint, e);
        }
      }
    }
    

    Joke

    Here is what a slimmed-down Lombok class would look like.

    package org.example.domain;
    
    import lombok.Value;
    
    @Value
    public class Joke {
      String type;
      String setup;
      String punchline;
      int id;
    }
    

    JokeService

    The service is responsible for using the API helper to fetch the data and marshal it into the appropriate type.

    package org.example.service;
    
    import lombok.RequiredArgsConstructor;
    import org.example.api.ApiHelper;
    import org.example.domain.Joke;
    import org.springframework.stereotype.Service;
    
    @Service
    @RequiredArgsConstructor  // Injects: apiHelper
    public class JokeService {
      private final ApiHelper apiHelper;
    
      public String[] getJokeTypes() {
        return apiHelper.fetchData("/types", String[].class);
      }
    
      public Joke getRandomJoke() {
        return apiHelper.fetchData("/jokes/random", Joke.class);
      }
    }
    

    Joke Controller

    Finally, the controller uses the service to wrap the API response in an HTTP response of OK (200).

    package org.example.web;
    
    import lombok.RequiredArgsConstructor;
    import org.example.domain.Joke;
    import org.example.service.JokeService;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api/jokes")
    @RequiredArgsConstructor  // Injects: jokeService
    public class JokeController {
      private final JokeService jokeService;
    
      @GetMapping("/types")
      public ResponseEntity<String[]> getJokeTypes() {
        String[] jokeTypes = jokeService.getJokeTypes();
        return ResponseEntity.ok(jokeTypes);
      }
    
      @GetMapping("/random")
      public ResponseEntity<Joke> getRandomJoke() {
        Joke joke = jokeService.getRandomJoke();
        return ResponseEntity.ok(joke);
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search