I have a response from URL which looks like:
{"seq":1,"id":"Test1","changes":[{"rev":"1-52f5cdf008ecfbadf621c2939af7bd80"}]}
{"seq":2,"id":"Test2","changes":[{"rev":"1-8ce403a89dc5e7cb4187a16941b3fb7d"}]}
{"seq":3,"id":"Test3","changes":[{"rev":"1-52as7ddfd8ecfbadf621c2939af7bd80"}]}
{"seq":4,"id":"Test4","changes":[{"rev":"1-6yy03a89dc5e7cb45677a16941b3fb7d"}]}
If the mapped object is String, then getting all the changes feed.
ResponseEntity<String> responseEntity = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, String.class);
Whereas, if I happen to use a custom Value object, somethings like:
public class KnChanges {
private long seq;
private String id;
private List changes;
with getter and setter methods, then I'm getting only the first doc change details. Even if the KnChanges[] (array) is used, only the first change is obtained.
Can you please help as to how the JSON list structure mentioned above can be mapped to an object?
Thanks
Harsha
Some people asked for a better answer with some explaination. So here it is:
As sujim mentioned: You need to
ParameterizedTypeReference<List<KnChanges>> responseType = new ParameterizedTypeReference<List<KnChanges>>() {};
ResponseEntity<List<KnChanges>> resp = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, responseType);
List<KnChanges> list = resp.getBody();
Explaination:
The last parameter of the exchange method call defines the class that gets instantiated when the response is received. The response data will then be mapped to the resulting object. So you need a List.class in fist place. Because you expect a JSON array. Now you need to define the type of the content of that List. Here Java's type erasure throws some stones in your way. As Java removes generic type information at compile-time, you can't just define the expected List to be a List<KnChanges>.class. "Luckily" there is a hack ;) And that hack is new ParameterizedTypeReference<List<KnChanges>>() {}. Provided that object the application is able to read the generic type information at runtime. And therefore is able to map the received data to your Java objects.
As a side-note: There a several implementations of that hack. It's commonly used for dependency injection or mapper systems, where type erasure can sometimes be an issue. Also Googles Guava offers an implementation. See the code for more information. There you can also learn how it's done, if you like.
ParameterizedTypeReference<List<KnChanges>> responseType = new ParameterizedTypeReference<List<KnChanges>>() {};
ResponseEntity<List<KnChanges>> resp = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, responseType);
List<KnChanges> list = resp.getBody();
Related
By inconsistent I mean that variable types can differ depending on the API response. So a named variable could be an Object, a List of Objects, or bizarrely even a String. I do not and cannot control the third-party API I'm consuming.
I'm using restTemplate.exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables), and the top-level responseType is consistent. It's in the child (and descendant) objects where the types may differ.
Am I stuck with pivoting to consuming the API response as a String, and do manual parsing? Or is there a way to handle the fact that the variable types might map differently (similar to how GSON supports custom serialisation / deserialisation).
Managed to find a way through this. I did have to read the API response as a String and take it from there. General steps:
restTemplate.exchange into String response body
Setup an ObjectMapper
Configure that to accept single values as arrays, and
empty strings as null objects
Read into the POJO of your choice
Now, this won't be ideal for everyone - in a perfect world you shouldn't have to relax the JSON parsing rules at all. This is all because I'm handling a very inconsistent API.
Rough code example is as-follows (as our internal stack is pretty complex, so I've had to drag bits out from classes here and there):
String exampleEndpoint = Constants.EXAMPLE_ENDPOINT;
ResponseEntity<String> responseEntity = restTemplate.exchange(uri.toString(), HttpMethod.GET, null, String.class);
String stringResponse = responseEntity.getBody();
ExamplePOJO examplePojo = null;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
try {
examplePojo = mapper.readValue(stringResponse, ExamplePOJO.class);
} catch (JsonProcessingException | NullPointerException ne) {
// JsonProcessingException is from readValue, NPE is to catch the string response
// being null in the event you don't want to let it bubble up further
logger.error(ne.getLocalizedMessage());
}
Imagine this simple controller method :
public ResponseEntity<?> findById(#Parameter(description = "id") #PathVariable String id) {
Optional<Model> model = repository.findById(id);
if(model.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("Model not found");
}
return ResponseEntity.status(HttpStatus.OK)
.body(model.get());
}
It returns the model if it is found or an error string message if not. The case could be more complexe then this.
So I'm returning a String or a Model types, which could not be bound to the generic type of ResponseEntity.
My question is why spring team designed this class as a generic type ? or am I using wrong this object ?
Among other reasons, ResponseEntity<T> is also returned by Spring's RestTemplate HTTP client. The generic type allows the client code to specify the type that should be used to interpret the HTTP response and get an appropriate Java object as the response body.
When you define your API you define what is the type of the object you will return in case of the operation goes OK, this is the object that you should use in the generic type of the response entity.
If you want to return different types you can remove the generic and return only a ResponseEntity and add to it whatever object you want
What if a XML webservice can respond with different xml structures? Eg an <OkResponse> and an <ErrorResponse>, having completely different fields?
ResponseEntity<Response> rsp = restTemplate
.postForEntity(url, new HttpEntity<>(xml, HEADERS), OkResponse.class);
Before sending the request, I don't know which type of response will come back. If I'm using OkResponse.class, I will get a ClassCastException if an ErrorResponse is returned.
How could I handle this?
The autogenerated beans are as follows:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
OkResponse.class,
ErrorResponse.class
})
public class AbstractResponse {
}
Use String.class
ResponseEntity<String> rsp = restTemplate
.postForEntity(url, new HttpEntity<>(xml, HEADERS), String.class);
String responseBody = (String)rsp.getBody();
Object response=mapper.readValue(responseBody, Class.forName(responseClass))
Once response body is obtained. make use of service class that you want to map and convert it using jackson mapper .Made use of reflection since the entity passed can be different/dynamic
RestTemplate uses Jackson for JSON serialization, and it supports inherited types though the #JsonTypeInfo annotation. But it requires that all responses have a common 'type' property. If there is no common property that all responses share, then I think you need to use the String approach, and use String.contains() to find a unique property to determine which response type it is.
I have a server that exposes data with the spring-data-rest project and now I am writing services to consume those data and I started with a generic service that will suit all the common needs, one of which is getting the Page object.
I configured my RestTemplate to use the Jackson2HalModule as suggested here.
I've tried a lot of combinations and I was only able to use consume it properly in a non-generic way like this:
PagedResources<Resource<Company>> response2 = restTemplate.exchange(getUrl(), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<PagedResources<Resource<Company>>>(){}).getBody();
But trying the same code with T didn't work (Resource links were deserialized but content of Resource object was null)
PagedResources<Resource<T>> response3 = restTemplate.exchange(getUrl(), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<PagedResources<Resource<T>>>(){}).getBody();
Generically I am only able to deserialize the Company data using the following code:
PagedResources<T> response1 = restTemplate.exchange(getUrl(), HttpMethod.GET, HttpEntity.EMPTY, PagedResources.class).getBody();
But this one doesn't deserialize the Resource object so the Company&Links data of what should be the Resource object are stored in a LinkedHashMap instead.
I also tried using the object mapper on the LinkedHashMap with data but I was unsuccessful. It's been a long day so I might be too close to see the correct way of doing this. I'll appreciate any help with this. Thank you.
The question: Is there a way of getting proper generics working in this case?
I have a generic response wrapper class:
public class Response <T> {
T response;
}
and unrelated classes to be wrapped:
public class ServiceResponse {
String someField;
}
When I make a service request, I get a JSON response that looks something like:
{ "code":200, "response":{"someField":"some text"} }
Now, all my service responses have the same outer wrapper, i.e., they all have:
{ "code":200, "timestamp":"....", "response":... }
But the actual format/type of the response field is different for each service request. When I deserialize the response, I need to know the type of the response field so I can create the appropriate instance, if the deserialization was done within Response, I could use:
response = new T(jsonParser);
However, I'm doing all of this from within a library that is driven by reflection, so I normally deserialize the whole tree with code like:
wrapper = deserializer.parseObject(Response<ServiceResponse>.class)
but, at this point my parseObject method can't correctly determine the type of T.
I can use something like:
Response<ServiceResponse> response = new Response<>();
Field field = response.getClass().getDeclaredField("response");
Type type = field.getGenericType();
which then tells me that response is of type T but what I actually need is ServiceResponse
Per this SO question I tried casting as ParameterizedType but that would actually seem to apply to a field of type Response<ServiceResponse> and not the actual field within (and it fails because type can't be cast as ParameterizedType)
Is there any way to determine (at run time) the raw type of response?
Eventually, I may wind up having to create an annotation providing more details about how to deserialize the field, probably by providing a function to do it, but would prefer a more transparent approach.
Another possibility might be to actually assign a void instance of T to response at initialization time and then I could grab the actual type from that...
Check out this post:
http://mydailyjava.blogspot.com/2013/06/advanced-java-generics-retreiving.html
It's actually exactly what you're looking for.
According to this, you'll just need to extend your Response class and then query the generic type of its super.