Get list of JSON objects with Spring RestTemplate
Asked Answered
A

15

250

I have two questions:

  • How to map a list of JSON objects using Spring RestTemplate.
  • How to map nested JSON objects.

I am trying to consume https://bitpay.com/api/rates, by following the tutorial from http://spring.io/guides/gs/consuming-rest/.

Angel answered 15/5, 2014 at 9:17 Comment(1)
Consider see this answer, specially if you want use generics list #36916323Graticule
S
253

Maybe this way...

ResponseEntity<Object[]> responseEntity = restTemplate.getForEntity(urlGETList, Object[].class);
Object[] objects = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();

Controller code for the RequestMapping

@RequestMapping(value="/Object/getList/", method=RequestMethod.GET)
public @ResponseBody List<Object> findAllObjects() {

    List<Object> objects = new ArrayList<Object>();
    return objects;
}

ResponseEntity is an extension of HttpEntity that adds a HttpStatus status code. Used in RestTemplate as well @Controller methods. In RestTemplate this class is returned by getForEntity() and exchange().

Shad answered 15/5, 2014 at 10:18 Comment(6)
That worked like a charm , thank you . Maybe you can direct me to some other tutorials or guides that I could read on this topic ?Angel
best to look here on stackoverflow for some code snippets and examples or visit the offial spring website...... TblGps[] a = responseEntity.getBody();Shad
Is it possible to this using generics? i.e. my method has a Class<T extends Foo> parameter and I would like to get a collection of T from the getForEntity method.Non
Yes it should work, but might not be out of the box depending on your spring/jackson version and your class types. Its all about serializing/deserializing generics - the http Request istself does not care what is transported.Shad
Also see forum.spring.io/forum/spring-projects/android/….Automate
@kamokaze, can you please take a look related question https://mcmap.net/q/118725/-could-not-send-request-using-resttemplate-the-same-as-in-the-postman-application/2674303 ?Kaitlynkaitlynn
I
412

First define an object to hold the entity coming back in the array.. e.g.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Rate {
    private String name;
    private String code;
    private Double rate;
    // add getters and setters
}

Then you can consume the service and get a strongly typed list via:

ResponseEntity<List<Rate>> rateResponse =
        restTemplate.exchange("https://bitpay.com/api/rates",
                    HttpMethod.GET, null, new ParameterizedTypeReference<List<Rate>>() {
            });
List<Rate> rates = rateResponse.getBody();

The other solutions above will also work, but I like getting a strongly typed list back instead of an Object[].

Impermanent answered 11/8, 2015 at 16:29 Comment(5)
This run's smoothly with Spring 4.2.3 and - as Matt said - has the big advantage of avoiding the Object[]Wendish
@Impermanent - which marshaller are you using to marshal the json into Rate objects? I am guessing that's what's happening here, at the time of the restTemplate.exchange a marshallar maps all the json values to the matching key names as properties in the Rate object. Hope my thought process is correct.Glynas
@Glynas Spring uses Jackson by default I believe.Collarbone
ParameterizedTypeReference is @deprecatedTetrode
@SarvarNishonboev the current ParameterizedTypeReference from springframework.core still seems fine: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…Apiculture
S
253

Maybe this way...

ResponseEntity<Object[]> responseEntity = restTemplate.getForEntity(urlGETList, Object[].class);
Object[] objects = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();

Controller code for the RequestMapping

@RequestMapping(value="/Object/getList/", method=RequestMethod.GET)
public @ResponseBody List<Object> findAllObjects() {

    List<Object> objects = new ArrayList<Object>();
    return objects;
}

ResponseEntity is an extension of HttpEntity that adds a HttpStatus status code. Used in RestTemplate as well @Controller methods. In RestTemplate this class is returned by getForEntity() and exchange().

Shad answered 15/5, 2014 at 10:18 Comment(6)
That worked like a charm , thank you . Maybe you can direct me to some other tutorials or guides that I could read on this topic ?Angel
best to look here on stackoverflow for some code snippets and examples or visit the offial spring website...... TblGps[] a = responseEntity.getBody();Shad
Is it possible to this using generics? i.e. my method has a Class<T extends Foo> parameter and I would like to get a collection of T from the getForEntity method.Non
Yes it should work, but might not be out of the box depending on your spring/jackson version and your class types. Its all about serializing/deserializing generics - the http Request istself does not care what is transported.Shad
Also see forum.spring.io/forum/spring-projects/android/….Automate
@kamokaze, can you please take a look related question https://mcmap.net/q/118725/-could-not-send-request-using-resttemplate-the-same-as-in-the-postman-application/2674303 ?Kaitlynkaitlynn
E
83

For me this worked

Object[] forNow = template.getForObject("URL", Object[].class);
    searchList= Arrays.asList(forNow);

Where Object is the class you want

Eureka answered 7/11, 2014 at 7:25 Comment(3)
This works even if you use a class and not Object like Coupon[] coupons = restTemplate.getForObject( url, Coupon[].class)Gigantopithecus
This can cause NPE if HTTP response body was empty (not [] but totally empty). So be careful and check for null (if (forNow != null)...).Articulate
Saved my ass :) Wondering what type is used by Jackson, when Object.class is specified in method getForObject().Vicinity
E
11

If you would prefer a List of POJOs, one way to do it is like this:

class SomeObject {
    private int id;
    private String name;
}

public <T> List<T> getApi(final String path, final HttpMethod method) {     
    final RestTemplate restTemplate = new RestTemplate();
    final ResponseEntity<List<T>> response = restTemplate.exchange(
      path,
      method,
      null,
      new ParameterizedTypeReference<List<T>>(){});
    List<T> list = response.getBody();
    return list;
}

And use it like so:

 List<SomeObject> list = someService.getApi("http://localhost:8080/some/api",HttpMethod.GET);

Explanation for the above can be found here (https://www.baeldung.com/spring-rest-template-list) and is paraphrased below.

There are a couple of things happening in the code above. First, we use ResponseEntity as our return type, using it to wrap the list of objects we really want. Second, we are calling RestTemplate.exchange() instead of getForObject().

This is the most generic way to use RestTemplate. It requires us to specify the HTTP method, optional request body, and a response type. In this case, we use an anonymous subclass of ParameterizedTypeReference for the response type.

This last part is what allows us to convert the JSON response into a list of objects that are the appropriate type. When we create an anonymous subclass of ParameterizedTypeReference, it uses reflection to capture information about the class type we want to convert our response to.

It holds on to this information using Java’s Type object, and we no longer have to worry about type erasure.

Erfurt answered 19/11, 2018 at 23:24 Comment(0)
U
9

You can create POJO for each entry like,

class BitPay{
private String code;
private String name;
private double rate;
}

then using ParameterizedTypeReference of List of BitPay you can use as:

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Employee>> response = restTemplate.exchange(
  "https://bitpay.com/api/rates",
  HttpMethod.GET,
  null,
  new ParameterizedTypeReference<List<BitPay>>(){});
List<Employee> employees = response.getBody();
Undercurrent answered 30/3, 2019 at 11:48 Comment(0)
Z
7

For those working with Spring + Kotlin, here's the translation:

val rates = restTemplate.exchange("https://bitpay.com/api/rates", HttpMethod.GET, null, object : ParameterizedTypeReference<List<Rate>>() {}).body!!
Zaria answered 20/4, 2022 at 13:32 Comment(1)
Another way in kotlin is to do: val rates = restTemplate.exchange("https://bitpay.com/api/rates", HttpMethod.GET, null, Array<Rate>::class.java).body!!Diapason
E
5

After multiple tests, this is the best way I found :)

Set<User> test = httpService.get(url).toResponseSet(User[].class);

All you need there

public <T> Set<T> toResponseSet(Class<T[]> setType) {
    HttpEntity<?> body = new HttpEntity<>(objectBody, headers);
    ResponseEntity<T[]> response = template.exchange(url, method, body, setType);
    return Sets.newHashSet(response.getBody());
}
Evidence answered 12/9, 2017 at 13:2 Comment(1)
Note: this requires GuavaAraby
R
5

3 alternative ways have mentioned here to retrieve list of objects. All of these will work flawlessly

@RequestMapping(value = "/emp2", produces = "application/json")
public List<Employee> getEmp2()
{
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    HttpEntity<String> entity = new HttpEntity<String>(headers);
    ResponseEntity<List<Employee>> response = restTemplate.exchange(
            "http://hello-server/rest/employees", HttpMethod.GET,entity, 
    new ParameterizedTypeReference<List<Employee>>() {});
    return response.getBody();
}

(OR)

@RequestMapping(value = "/emp3", produces = "application/json")
public List<Employee> getEmp3()
{
    Employee[] empArray = restTemplate.getForObject("http://hello-server/rest/employees", Employee[].class);
    List<Employee> emp= Arrays.asList(empArray);
    return emp;
}

(OR)

    @RequestMapping(value = "/emp4", produces = "application/json")
public Employee[] getEmp4()
{
    ResponseEntity<Employee[]> responseEntity = restTemplate.getForEntity("http://hello-server/rest/employees", Employee[].class);
    Employee[] empList = responseEntity.getBody();
    //MediaType contentType = responseEntity.getHeaders().getContentType();
    //HttpStatus statusCode = responseEntity.getStatusCode();
    return  empList;
}

Employee.class

public class Employee {

private Integer id;
private String name;
private String Designation;
private String company;

//getter setters and toString()

}
Reber answered 19/7, 2021 at 16:41 Comment(0)
R
3

My big issue here was to build the Object structure required to match RestTemplate to a compatible Class. Luckily I found http://www.jsonschema2pojo.org/ (get the JSON response in a browser and use it as input) and I can't recommend this enough!

Restrainer answered 29/1, 2018 at 23:33 Comment(0)
F
2

i actually deveopped something functional for one of my projects before and here is the code :

/**
 * @param url             is the URI address of the WebService
 * @param parameterObject the object where all parameters are passed.
 * @param returnType      the return type you are expecting. Exemple : someClass.class
 */

public static <T> T getObject(String url, Object parameterObject, Class<T> returnType) {
    try {
        ResponseEntity<T> res;
        ObjectMapper mapper = new ObjectMapper();
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        ((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(2000);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<T> entity = new HttpEntity<T>((T) parameterObject, headers);
        String json = mapper.writeValueAsString(restTemplate.exchange(url, org.springframework.http.HttpMethod.POST, entity, returnType).getBody());
        return new Gson().fromJson(json, returnType);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

/**
 * @param url             is the URI address of the WebService
 * @param parameterObject the object where all parameters are passed.
 * @param returnType      the type of the returned object. Must be an array. Exemple : someClass[].class
 */
public static <T> List<T> getListOfObjects(String url, Object parameterObject, Class<T[]> returnType) {
    try {
        ObjectMapper mapper = new ObjectMapper();
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        ((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(2000);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<T> entity = new HttpEntity<T>((T) parameterObject, headers);
        ResponseEntity<Object[]> results = restTemplate.exchange(url, org.springframework.http.HttpMethod.POST, entity, Object[].class);
        String json = mapper.writeValueAsString(results.getBody());
        T[] arr = new Gson().fromJson(json, returnType);
        return Arrays.asList(arr);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

I hope that this will help somebody !

Facient answered 15/12, 2018 at 8:35 Comment(0)
E
2

In my case I preferred to extract a String then browse the context using JsonNode interface

    var response =  restTemplate.exchange("https://my-url", HttpMethod.GET, entity,  String.class);
    if (response.getStatusCode() == HttpStatus.OK) {
        var jsonString = response.getBody();
        ObjectMapper mapper = new ObjectMapper();
        JsonNode actualObj = mapper.readTree(jsonString);           
        
        System.out.println(actualObj);  
    }

or quickly

ObjectNode actualObj= restTemplate.getForObject("https://my-url", ObjectNode.class);

then read inner data with path expression i.e.

boolean b = actualObj.at("/0/states/0/no_data").asBoolean();
Existence answered 9/9, 2020 at 19:31 Comment(0)
G
1

Consider see this answer, specially if you want use generics in List Spring RestTemplate and generic types ParameterizedTypeReference collections like List<T>

Graticule answered 20/11, 2018 at 18:16 Comment(0)
U
0

As a general module, Page<?> object can be deserialized by module just like JodaModule, Log4jJsonModule and so on. refer to this answer of mine.JsonMappingException when testing endpoints with Pageable field

Untraveled answered 7/6, 2021 at 6:12 Comment(0)
C
0

An Easier Way:
I will show you guys both with Authorization heard and without Authorization header:

  1. Without Authorization:
    a. Do Dependency Injection(Constructor Injection): You can also prefer field injection. I considered constructor injection.
public class RestTemplateService {
private final RestTemplate template;
public RestTemplateService(RestTemplate template) {
    this.template = template;
  }
}

b. Invoke getList() method:

public ResponseEntity<List> getResponseList(String url, HttpMethod type) {
    return template.exchange(url, type, new HttpEntity<>(new HttpHeaders()), List.class);
  }
  1. With Authorization: I love small methods. So I segregated the functionalities as such:
 public ResponseEntity<List> getResponse(String url, HttpMethod type) {
    return template.exchange(url, type, getRequest(getHeaders(USERNAME, PASS)), List.class);
  }

 private HttpEntity<String> getRequest(HttpHeaders headers) {
    return new HttpEntity<>(headers);
  }

 private HttpHeaders getHeaders(String username, String password) {
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + new String(Base64.encodeBase64((username + ":" + password).getBytes())));
    return headers;
  }


Hopefully the problem will be resolved!

Crowboot answered 14/1, 2022 at 9:26 Comment(0)
T
-1

I found work around from this post https://jira.spring.io/browse/SPR-8263.

Based on this post you can return a typed list like this:

ResponseEntity<? extends ArrayList<User>> responseEntity = restTemplate.getForEntity(restEndPointUrl, (Class<? extends ArrayList<User>>)ArrayList.class, userId);
Trevatrevah answered 14/5, 2015 at 21:44 Comment(1)
This won't work, because due to erasure no type parameter information is passed to getForEntity. Also (Class<? extends ArrayList<User>>) ArrayList.class gives an "incompatible types" compile error.Broomcorn

© 2022 - 2024 — McMap. All rights reserved.