Generics with Spring RESTTemplate
Asked Answered
D

4

93

I have a class like that:

public class Wrapper<T> {

 private String message;
 private T data;

 public String getMessage() {
    return message;
 }

 public void setMessage(String message) {
    this.message = message;
 }

 public T getData() {
    return data;
 }

 public void setData(T data) {
    this.data = data;
 }

}

and I use resttemplate as follows:

...
Wrapper<Model> response = restTemplate.getForObject(URL, Wrapper.class, myMap);
Model model = response.getData();
...

However it throws a:

ClassCastException

I read that: Issue when trying to use Jackson in java but didn't help. There are some topics related to my problem etc.: https://jira.springsource.org/browse/SPR-7002 and https://jira.springsource.org/browse/SPR-7023

Any ideas?

PS: My error is that:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to a.b.c.d.Model

I think resttemplate can not understand my generic variable and maybe it accepts it as an Object instead of generic T. So it becomes a LinkedHashMap. You can read from it here It says that when explaining from what it marshalls to:

JSON Type | Java Type

object | LinkedHashMap

Disentwine answered 13/11, 2011 at 1:27 Comment(4)
Plz put the exception trace as well.Adler
I suppose solution for this is added in 3.2 SPR-7023Affranchise
Did you ever find a solution for this? I'm using ParameterizedTypeReference and while it works when I pass in Map<String, String> it doesn't work when I pass in my concrete class that represents data in your model.Bun
Did you a find a solution??Woken
U
176

ParameterizedTypeReference has been introduced in 3.2 M2 to workaround this issue.

Wrapper<Model> response = restClient.exchange(loginUrl, 
                          HttpMethod.GET, 
                          null, 
                          new ParameterizedTypeReference<Wrapper<Model>>() {}).getBody();

However, the postForObject/getForObject variant was not introduced.

Unboned answered 16/1, 2014 at 9:44 Comment(4)
I tried another optioned for your guys,@alexanoid . Just first get the response as String ,then use Jackson to parse the string to generics object ,see : String body = restTemplate.exchange(request,String.class).getBody(); ObjectMapper mapper=new ObjectMapper(); DataTablesOutput<EmployeeResponse> readValue = mapper.readValue(body, DataTablesOutput.class);Budworth
Why Model cannot here be also generic?, etc. like this: Model<InnerModel>Shea
To complement Alter's answer you can use: mapper.readValue(body, new TypeReference<Wrapper<Model>>(){});Exhort
My question also why we can't make Model also generic here ?Sika
W
13

The only thing I think you could do is creating a new class that extends Wrapper and uses model as a generic.

class WrapperWithModel extends Wrapper<Model>{};

WrapperWithModel response = restTemplate.getForObject(URL, WrapperWithModel.class);

It's not the best solution, but at least you won't have to unmarshall manually the response.

Windywindzer answered 22/1, 2014 at 16:53 Comment(2)
I personally don't like this solution but right now I can't upgrade spring to use its new parameterizedtype object (jira.springsource.org/browse/SPR-7023), so this is good enough. thanksKindergartner
it must be static classVehemence
C
2

Do not use generics with RestTemplate. Wrap request and response object with wrapper object that will hide the generics.

Cooperstein answered 19/7, 2012 at 13:17 Comment(0)
A
0

If you want to do it in a generic methode you can use ResolvableType from spring.

public <T> Wrapper<T> getObject(String loginUrl, Class<T> clazz) {
    ResolvableType resolvableType = ResolvableType.forClassWithGenerics(Wrapper.class, clazz);
    return restClient.exchange(loginUrl, 
                          HttpMethod.GET, 
                          null, 
                          ParameterizedTypeReference.forType(resolvableType.getType()))
                     .getBody();
}
Alcinia answered 25/4, 2024 at 16:6 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.