When I run
ResponseEntity<JSONObject> response = queryToRest.exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), JSONObject.class);
the function returns 200 and an empty body {}
I need to run
ResponseEntity<String> response = queryToRest.exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), String.class);
to get my content and then convert it to a JSONObject.
How to directly have a JSONObject? Do I need to add some parameters to my RestTemplate queryToRest object?
This used to work fine under spring 1.5 but not right now with spring 2.1 (idk if it is the reason)
I am developing an application using Spring Boot (with Java).
This application has to call several external services and each of these services requires a complicated body (in json or xml) (this input can vary! The fields I pass to it are not required so sometimes I might even pass a subset of these fields). These are examples of inputs that services can receive:
{
"field1": "string",
"field2": "string",
"field3": "string",
"field4": 0,
}
<input>
<input1>my_string</input1>
<input2>my_string</input2>
</input>
I use RestTemplate to make HTTP calls. This is an example. I use a Java String to model the HTTP body (but it has the big defect that it is not editable but hard-coded!):
String Jsonbody = "{\r\n"
+ " \"field1\": \""+myString1+"\"\r\n"
+ " \"field2\": \""+myString2+"\"\r\n"
+ "}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(Jsonbody, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<MyResponseClass> response = restTemplate.postForEntity(url, request, MyResponseClass.class);
It is very bad to have a body hard-coded like this in the JsonBody variable. What is the way to have an object in which I dynamically insert strings and which automatically creates a JSON object (which I can then convert to a string to put in the .postForEntity method)? The same issue for XML input types.
There are multiple libraries as Gson or Jackson that actually allow you to work with JSON as they are Java Objects to finally serialize them as a body.
If you want to give it a try, you would just need to include Gson as dependency:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
Then you'd have to create a POJO for your POST body:
public class JsonBody {
private List<String> fields = new LinkedList<>();
void addField(String field) {
fields.add(field);
}
}
And feel free to add as many String as you want that it will be parsed afterwards.
JsonBody jsonBody = new JsonBody();
Gson gson = new Gson();
jsonBody.addField(myString1);
jsonBody.addField(myString2);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(gson.toJson(jsonBody), headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<MyResponseClass> response = restTemplate.postForEntity(url, request, MyResponseClass.class);
This would set your JSON to:
{
fields: [
contentMyString1,
contentMyString2
]
}
Taking the previous example into account, you can just modify and format your JSON as you want in the form of a POJO and use the Gson::toJson(String) function from Gson.
Of course if you just create a POJO as:
public class JsonBody {
private String field1;
private String field2;
private String field3;
private Integer field4;
}
Then the output would be the same one than you have in your initial example.
Use the ObjectMapper class like this ...
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);
The output will be
{"color":"yellow","type":"renault"}
Here's some more data on the topic https://www.baeldung.com/jackson-object-mapper-tutorial
Same approach is used for mapping objects into XML (https://www.baeldung.com/jackson-xml-serialization-and-deserialization)
I'm using RestTemplate and SpringBoot 2.0.
I have this class which represents a custom JSON response to rest calls.
MyCustomResponse<T> {
private T content;
public T getContent() {
return content;
}
}
The code snippet below is a example of use:
String LOCALHOST = "http://localhost:8900";
final HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, "application/json");
String url = String.format("%s/ticket/%s", LOCALHOST, protocol);
final HttpEntity<Ticket> request = new HttpEntity<>(headers);
This works fine:
ResponseEntity<MyCustomResponse> response =
testRestTemplate.getForEntity(url, RespostaPadrao.class);
But it's not what I need, because getContent returns a object that looks like:
Map< Map < String, Map < String, Map < String, Map < String, Integer >>>>>
and because I want to test a Ticket object instead.
In this line, can I use reflection to parametrize the response or this is impossible?
ResponseEntity<MyCustomResponse> response =
testRestTemplate.getForEntity(url, MyCustomResponse.class);
I know how to fix it with ResponseEntity<String> and parse the JSON to my Ticket. But what I really need is know if is possible delegate this task to RestTemplate or not.
Thanks!
I have two services (one service calls an end point localhost:7000/create to send a json and want expects a json )
Called service is something like this (Data is pojo class) :
#RequestMapping(value="/create",consumes="application/json",produces = "application/json",method = RequestMethod.POST)
#ResponseBody
public Data responseForPayload(#RequestBody String data) {
Data data= new Data();
data.setAccountId("45");
return data;
}
and the calling service restTemplate call is like this (running in :9000):
String ResourceUrl = "http://localhost:7000/create";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
List list= new ArrayList();
list.add(MediaType.APPLICATION_JSON);
HttpEntity httpEntity = new HttpEntity(downstreamPayload, headers);
ResponseEntity<JSONObject> response = restTemplate.exchange(ResourceUrl,
HttpMethod.POST, httpEntity, JSONObject.class);
but i am getting response as {}, when I use string instead of JSON it works fine. I used postman to just to call the 127.0.0.1:7000/create, it worked fine with returning the expected as json
What is my mistake here?
Thanks
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I'm invoking a rest service that returns JSON like this:
{
"some.key" : "some value",
"another.key" : "another value"
}
I would like to think that I can invoke this service with a java.util.Map as the response type but that's not working for me. I get this exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map]
Should I just specify String as the response type and convert the JSON to a Map?
Edit I
Here's my restTemplate call:
private Map<String, String> getBuildInfo(String buildUrl) {
return restTemplate.getForObject(buildUrl, Map.class);
}
Here's how I'm setting up the restTemplate:
#PostConstruct
public void initialize() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
requestWrapper.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return execution.execute(requestWrapper, body);
}
});
restTemplate.setInterceptors(interceptors);
}
Edit II
Full error message:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at idexx.ordering.services.AwsServersServiceImpl.getBuildInfo(AwsServersServiceImpl.java:96) ~[classes/:na]
RestTemplate has a method named exchange that takes an instance of ParameterizedTypeReference as parameter.
To make a GET request that returns a java.util.Map, just create an instance of an anonym class that inherits from ParameterizedTypeReference.
ParameterizedTypeReference<Map<String, String>> responseType =
new ParameterizedTypeReference<>() {};
You can then invoke the exchange method:
RequestEntity<Void> request = RequestEntity.get("http://example.com/foo")
.accept(MediaType.APPLICATION_JSON).build();
Map<String, String> jsonDictionary = restTemplate.exchange(request, responseType).getBody();
As I had previously noted, your error message is showing us that you are receiving application/octet-stream as a Content-Type.
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
As such, Jackson's MappingJackson2HttpMessageConverter cannot parse the content (it's expecting application/json).
Original answer:
Assuming your HTTP response's Content-Type is application/json and you have have Jackson 1 or 2 on the classpath, a RestTemplate can deserialize JSON like you have into a java.util.Map just fine.
With the error you are getting, which you haven't shown in full, either you've registered custom HttpMessageConverter objects which overwrite the defaults ones, or you don't have Jackson on your classpath and the MappingJackson2HttpMessageConverter isn't registered (which would do the deserialization) or you aren't receiving application/json.
I think you can achieve what you're aiming for simply using the RestTemplate and specifying a JsonNode as the response type.
ResponseEntity<JsonNode> response =
restTemplate.exchange(url, HttpMethod.GET, entity, JsonNode.class);
JsonNode map = response.getBody();
String someValue = map.get("someValue").asText();
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I ended up getting the contents as a String and then converting them to a Map like this:
String json = restTemplate.getForObject(buildUrl, String.class);
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
try {
//convert JSON string to Map
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
} catch (Exception e) {
logger.info("Exception converting {} to map", json, e);
}
return map;
I know its old, but just for other people that may visit this topic:
If you want to register some additional converters with RestTemplateBuilder you also have to explicitly register default ones
#Bean
public RestTemplateBuilder builder() {
return new RestTemplateBuilder()
.defaultMessageConverters()
.additionalMessageConverters(halMessageConverter());
}
private HttpMessageConverter halMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jackson2HalModule());
TypeConstrainedMappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
halConverter.setObjectMapper(objectMapper);
return halConverter;
}
This worked 100% for me
in client
Map<String, Object> mapRespuesta = new HashMap<>();
mapRespuesta.put("mensaje", "Process completed successfully");
mapRespuesta.put("idResponse", id);
return new ResponseEntity<Map<String, Object>>(mapRespuesta, HttpStatus.OK);
in which it makes the connection
ResponseEntity<Map> result = restTemplate.postForEntity(url, params, Map.class);
String id = result.getBody().get("idResponse").toString();
#GetMapping(value = "getSunny/{userId}")
public Map<String, SunnyVO> getSunny(#PathVariable int sunnyId) {
Map<String, SunnyVO> newObj = new HashMap<String, SunnyVO>();
final String url = "http://localhost:8085/Sunny/getSunny/{sunnyId}";
RestTemplate restTemplate = new RestTemplate();
newObj = restTemplate.getForObject(url, Map.class, sunnyId);
return newObj;
}
It is working for me ...