Search code examples
javaspring-bootspring-mvcspring-cloudspring-cloud-feign

Get a list of objects in JSON with Spring Cloud Open Feign


I have an application that when I call one of its services it returns the following JSON:

{
     "suppliers": [
         {
             "name": "Joaquim",
             "email": "joaquim@email.com",
         },
     {
             "name": "Manoel",
             "email": "manoel@email.com",
         }
     ]
}

To access this data I use a Feign client that has the following method:

@FeignClient(name = "suppliersClient", url = "www.url-example.com.br")
// ...
List<SuppliersDTO> searchSuppliers(@RequestParam("city") String city);

Depending on the city it returns a list of suppliers. This code is a simple example to explain to you what I'm trying to do.

My DTO Suppliers class has the name and email fields, in addition to getters and setters.

public class SuppliersDTO {

     String name;
     String email;

     // GETTERS AND SETTERS

With the code like this, when using the Feign Client I get a 500 Internal Server Error with the information:

"Error while extracting response for type [java.util.List<com.domain.store.dto.SuppliersDTO>] and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<com.domain.store.dto.SuppliersDTO>` from Object value (token `JsonToken.START_OBJECT`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<com.domain.store.dto.SuppliersDTO>` from Object value (token `JsonToken.START_OBJECT`)\n at [Source: (PushbackInputStream); line: 1, column: 1]"

From what I researched and read, the error occurs because the service's JSON returns a supplier object and inside it has a list. That's why jackson can't bind to my List<SuppliersDTO>. To get around this I created another class:

public class SuppliersListDTO {
    List<SuppliersDTO> suppliers;
    // GETTERS E SETTERS

Now my Feign returns SuppliersListDTO and not List<SuppliersDTO>:

@FeignClient(name = "SuppliersClient", url = "www.url-example.com.br")
// ...
SuppliersListDTO searchSuppliers(@RequestParam("city") String city);

This way it's working, the bind is done and I return a SuppliersListDTO to the user. But it doesn't seem like the most correct way to work with a list received from a JSON. Do I really have to create two DTO class for this, one of them with just a variable (List<SuppliersDTO> suppliers) and its getter and setter? With your experience, would you recommend another approach?


Solution

  • It doesn't matter the response format, can be a Collection or not... Follow this code :

    import lombok.Data;
    import java.util.List;
    
    @Data
    public class DictionaryDto {
    
        private final String domainDescription;
        private final List<DictionaryKeyDto> keys;
    
    }
    
    
    import lombok.Data;
    
    @Data
    public class DictionaryKeyDto {
    
        private final String keyCode;
        private final String keyValue;
        private final String keyExtendedValue;
    
    }
    
    
    import com.james.domain.client.DictionaryDto;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.validation.constraints.NotEmpty;
    
    @FeignClient(name = "${be.client.name}", url = "${be.client.url}")
    public interface BeClient {
    
        @GetMapping("dictionary/{domainCode}")
        public DictionaryDto getDictionaryByDomainCode(@PathVariable @NotEmpty String domainCode);
    
    }
    
    
    
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableFeignClients
    public class StandAloneApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(StandAloneApplication.class, args);
        }
    
    }
    

    i used this mvn dependency :

    org.springframework.cloud spring-cloud-starter-openfeign