I am working on a Microservices Project in Java, using Spring Boot and Eureka. I came across a scenario where I fetch the data from another microservice using:
List<Rating> ratings=(List<Rating>) restTemplate.getForObject("http://localhost:8083/microservices/rating/get-all-ratings-by-user?userId=ddb8e2a9-ac6f-460d-a43e-eae23d18450c", Map.class).get("data");
I get a warning on this line:
<Map> Map org.springframework.web.client.RestTemplate.getForObject(String url, Class<Map> responseType, Object... uriVariables) throws RestClientException
Type safety: Unchecked cast from Object to List<Rating> Java(16777761)
Potential null pointer access: The method getForObject(String, Class, Object[]) may return null Java(536871831)
Explanation
I get the following response from the URL I used above:
{
"data": [
{
"ratingId": "6140b240-9a97-430d-92b9-0fcfa8edc96f",
"userId": "ddb8e2a9-ac6f-460d-a43e-eae23d18450c",
"hotelId": "1093aa3f-8529-4330-8ce8-caa82546200b",
"rating": 4,
"feedback": "Died peacefully"
}
],
"message": "Success",
"code": "2000"
}
Aim
I want to extract the list of Rating
objects from the response's data
field and store it as a List<Rating>
. Further, I want to iterate over it and perform other operations.
Map
(Map.class passed in getForObject()
method).Map
using .get("data")
.List<Rating>
.My Question
Using this above approach I am able to obtain a list of objects of Rating
class. But, can somebody explain how this map (obtained using .get("data")
) automatically gets converted to List<Rating>
using simple type conversion? The code doesn't seem to use any Object Mapper like Jackson. Also, I am getting a warning. Is there any way to remove it? I can send the List<Rating>
as it is to a GET request. But if I try to use a method on the List
, I get errors, see below
Follow Up
stream()
and map()
on the List<Rating>
, I obtained above:ratings=ratings.stream().map(rating->{
// api call to hotel service to obtain the hotel
Hotel hotel=(Hotel) restTemplate.getForEntity("http://localhost:8086/microservices/hotel/get-hotel-by-id?hotelId="+rating.getHotelId(), Map.class).getBody().get("data");
logger.info("fetched hotel: ", hotel);
rating.setHotel(hotel);
}).collect(Collectors.toList());
But I get a compile-time error on the .map()
:
<R> Stream<R> java.util.stream.Stream.map(Function<? super Rating, ? extends R> arg0)
The method map(Function<? super Rating,? extends R>) in the type Stream<Rating> is not applicable for the arguments ((<no type> rating) -> {}) Java(67108979)
forEach()
gives class cast exception:ratings.forEach((rating)->{
// api call to hotel service to obtain the hotel
Hotel hotel=(Hotel) restTemplate.getForEntity("http://localhost:8086/microservices/hotel/get-hotel-by-id?hotelId="+rating.getHotelId(), Map.class).getBody().get("data");
logger.info("fetched hotel: ", hotel);
rating.setHotel(hotel);
});
Error on forEach()
:
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.example.user_service.entities.Rating (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.example.user_service.entities.Rating is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @db2af5f)
at java.base/java.util.ArrayList.forEach(Unknown Source) ~[na:na]
at com.example.user_service.ServiceImpl.UserServiceImpl.getUser(UserServiceImpl.java:84) ~[classes/:na]
at com.example.user_service.controller.UserController.getUserById(UserController.java:53) ~[classes/:na]
You are overcomplicating the task, you can convert the json response from your RestTemplate
to a String
value, then extract the data part inside of it with jackson library like below:
JsonNode root =mapper.readTree(json); //<--convert the json string to a JsonNode
JsonNode data = root.at("/data"); //<-- selecting the "data" part
//conversion to List<Rating> avoid problems due to list type erasure
//with the help of jackson TypeReference class
List<Rating> ratings = mapper.convertValue(data, new TypeReference<List<Rating>>() {});
This is achieved using the JsonNode#at
method that locates the specific node with data label inside your json, to convert it to a List<Rating>
it is necessary to use TypeReference
to instantiate reference to generic type List<Rating>
.