How to handle dynamic request body without creating request class? - java

I have an api endpoint with request body like this.
{
"employeeId" : "1234",
"empName" : "John"
}
The fields are dynamic. So instead of creating a request class I am passing request body as HashMap<String, String> as below.
#PostMapping
public ResponseEntity<EmployeeResponse> getEmployees(#RequestBody HashMap<String, String> queryParams){
}
Now my requirement is to add something like this in the request body along with other dynamic fields.
{
"empAwardsReceived" : ["On the spot award", "Best employee award"]
}
How can we handle this?
Can someone please help?

You could try to use #RequestBody String queryParams instead of #RequestBody HashMap<String, String> queryParams.
Then you could try to look at eg. the Jackson library, which would help you to parse JSON as you want.
You probably could use code like presented below.
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.valueToTree(queryParams);
Then you could manipulate on JsonNode by using methods mentioned eg. in this article.
So adding a new JSON node should not be a problem.

I changed RequestBody from HashMap<String, String> to HashMap<String, Object> and it worked.

Related

How to use RestTemplate for a POST request for a complex body in Java?

I'm working with RestTemplate in a Spring Boot project and I try to call a POST. And I'm using this code:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("email", "abc#email.com");
map.add("id", "1234567");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<Employ> response = restTemplate.postForEntity(
"url", request , Employ.class );
Now I want to use RestTemplate to consume a POST request but for a complex object. So the body should look like this:
{
"SomeThing":{
"Expr":"cdjhcdjh"
},
"SomeInfo":{
"Progr":"EVZpoiuyt",
"Other": "{code: \"abcd\", password:\"12345\"}"
}
}
How can I create the Map for this body? Any feedback will be apreciated. Thank you!
You should create Java objects (POJO) representing the data you would like to send via RestTemplate.
For example:
public class ObjectToPost {
private SomeThing someThing;
private SomeInfo someInfo;
// getters & setters
}
public class SomeThing {
private String expr;
// getters & setters
}
public class SomeInfo {
private String progr;
private String other;
// getters & setters
}
Then you could use this object in your RestTemplate.
ObjectToPost obj = new ObjectToPost();
obj.setSomeThing(...)
obj.setSomeInfo(...)
restTemplate.postForEntity("url", obj , Employ.class);
You could also use a Map but it will make it quite complex. If you really would like to achieve it with a Map you should create a <String,Object> map like:
MultiValueMap<String, Object> map= new LinkedMultiValueMap<String, Object>();
MultiValueMap<String, MultiValueMap<String, Object>> somethingMap = new LinkedMultiValueMap<String, Object>();
something.put("SomeThing", Map.of("Expr","cdjhcdjh");
map.put(somethingMap);
My suggestion is to use some other Http client and not RestTemplate. So if you have to use RestTemplate this answer is not relevant. I wrote my own Http client, and while it might be limited in what it can do its major advantage is simplicity. Your code may look like:
HttpClient client = new HttpClient();
String response = client.sendHttpRequest("www.myurl.com", HttpClient.HttpMethod.POST, "{\"yourJson\":\"here\"");
Here is Javadoc for HttpClient class. The library can be obtained as Maven artifacts or on Github (including source code and javadoc)

Spring Boot 2 WebClient response convert JSON to HashMap

I would like to get a response from WebClient and convert it to just a Map without creating any Class for the response. Is it possible? So I would like something like this below. The code below is not a valid code, it is just an idea of what I want.
public Map<String, String> someFunction() {
return webClient.post()
.uri("/some/path")
.retrieve()
.bodyToFlux(HashMap.class)
.block();
If you're interested in saving a LOC, you may want to look at a core Spring Framework class: ParameterizedTypeReference<T>, found here.
public Map<String, String> someFunction() {
return webClient.post()
.uri("/some/path")
.retrieve()
.bodyToFlux(new ParameterizedTypeReference<Map<String,String>>(){})
.block();
}
Cheers.
I would first try getting the response object into a String and also make sure I am accepting JSON type in return. Once I get the respone into a String, you can try using fasterxml's jackson databind library which can convert a JSON string into Hashmap.
For example
ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"abc\", \"age\":25}";
Map<String, Object> map = new HashMap<String, Object>();
// convert JSON string to Map
map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});
System.out.println(map);
Here is the databind library and core library java docs link
jackson-databind
jackson-core
I solved it like this:
public Map<String, String> someFunction() {
return webClient.post()
.uri("/some/path")
.retrieve()
.bodyToFlux(TypedMap.class)
.block();
}
private static class TypedMap extends HashMap<String, String>{}

How Jackson deserialize a Input Map object in Spring Boot Controller

I have this simple controller that receives a map as input:
#RequestMapping(value = "/providers", method = RequestMethod.PATCH, headers = "Accept=application/json")
#ResponseBody
#Transactional
public Map<String, Object> updateProvider(#RequestBody Map<String, Object> updates,
UriComponentsBuilder uriComponentsBuilder, final HttpServletRequest request) {
return updates;
}
and I have this property configured in Spring Boot in the application.properties file:
spring.jackson.default-property-inclusion=non_empty
Then, if I make a PATCH request with following JSON object.
{
"name":"frndo",
"lastname":""
}
The content of result in the RESPONSE is:
{
"name":"frndo"
}
But the content of input in the REQUEST is:
{
"name":"frndo",
"lastname":""
}
My question is, Why the content in the REQUEST is different in the RESPONSE if to serialize the Map object you have a global configuration like:
spring.jackson.default-property-inclusion=non_empty
Precisely why when #RequestBody Map<String, Object> updates arrives have the name and lastname fields if lastname is empty?.
In the RESPONSE you can see the effect of the configuration but in the REQUEST is not seen. What is the explanation of this, if the mapper had to convert the JSON to a java object, and in that process the global configuration had to be applied?
I expected to have the same content in the REQUEST and the RESPONSE
Many thanks!

Converting a POJO to Form Data in Java

I have a POJO of the form:
#Data
public class BaseRequest {
private String type;
private Map<String, Object> details;
private Map<String, Object> signature;
}
I have a service running which only accepts Content Type: "application/x-www-form-urlencoded".
I have written a client in Java which uses Spring's RestTemplate to make calls.
public String getInvoice(BaseRequest req, String url) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<BaseRequest> httpEntity = new HttpEntity<BaseRequest>(req, headers);
String response = this.restTemplate.postForObject(url, httpEntity, String.class);
return response;
}
However, it throws an error:
org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [com.x.y.z.BaseRequest] and content type [application/x-www-form-urlencoded]
It works if I set the content type as JSON:
headers.setContentType(MediaType.APPLICATION_JSON);
I know it works for JSON because I have configured my RestTemplate Bean with JacksonHTTPMessageConverter. So I can easily convert POJOs to application/json. However, I am not able to figure out how to do that with application/x-www-form-urlencoded.
I've been searching this for awhile now, and the only solution which I've found is to write my own converter to convert my BaseRequest class to Spring's MultiValueMap, and then Spring's FormHttpMessageConverter will automatically handle it. But I want to avoid doing that. Is there any other way around this?
Any leads would be appreciated. Thanks!
EDIT:
My question is different from #JsonProperty not working for Content-Type : application/x-www-form-urlencoded. The conversion happening there is about accepting data in application/x-www-form-urlencoded and converting it to a POJO. My question is about converting a POJO to application/x-www-form-urlencoded while using Spring's resttemplate to make calls. And like I mentioned, I know I can achieve this by writing my own converter to convert my POJO to Spring's MultiValueMap. However, I want to know if I can avoid doing this.
EDIT:
Dump of $_POST on the API when I send my data as MultiValueMap<String, Object>:
"array(0) {
}"
Dump of $_POST on the API when I send my data through Postman in the correct format:
"array(2) {
["type"]=>
string(16) "abcd"
["details"]=>
array(1) {
["template_file"]=>
string(16) "x.html"
}
}"
Try to convert your nested object in request payload to the org.springframework.util.MultiValueMap. Add and implement converter method in your POJO
public class BaseRequest {
// ...
public MultiValueMap<String, Object> toMap() {
MultiValueMap<String, Object> result = new LinkedMultiValueMap<>();
result.add("type", type);
result.put("details", details);
result.put("signature", signature);
return result;
}
}
Now use it during request creation
HttpEntity<BaseRequest> httpEntity = new HttpEntity<BaseRequest>(req.toMap(), headers);
That is caused because inside FormHttpMessageConverter which performs actual conversion method canRead(Class<?>, MediaType) checks if MultiValueMap.class.isAssignableFrom(clazz) where clazz is your payload object. In your case it failed, so FormHttpMessageConverter skipped.
Hope it helps!

How to loop through JSON response in Spring MVC Controller

I will like to use the JSON response inside a controller. I am calling a method that returns the JSON. See my code below. Please how do I loop through the Json object returned inside my controller. I need to use the properties like sending mail to the email addresses from another method inside my controller .
My method that does that returns the JSON :
#ResponseBody
private ResponseEntity<?> queryIsw(String ref, String amt) throws Exception{
String pdtid = "62";
String salt = "D3D1D05AFE42AD50818167EAC73C109168A0F108";
RestTemplate restt = new RestTemplate();
String uri = "https://bestng.com/gettransaction.json";
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("productid", pdtid);
params.add("transactionreference", ref);
params.add("amount", amt);
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(uri).queryParams(params).build();
URI oro = uriComponents.toUri();
HttpHeaders hea = new HttpHeaders();
String hs = pasher(pdtid, ref, salt);
hea.add("hash", hs);
HttpEntity<String> hent = new HttpEntity<String>(hea);
ResponseEntity<Object> resp = restt.exchange(oro, HttpMethod.GET, hent, Object.class);
return resp;
}
Below is my call to this method above from another method :
ResponseEntity<?> dres = queryIsw(dref,ama);
Kindly explain how I can use properties of 'dres' returned in my controller .
Thanks
Try taking a look at the Jackson JSON to Java mapping tools and specifically the ObjectMapper. It can convert a properly formatted JSON string into an object hierarchy from which you can pull out the data that you need. Jackson is a frequently used tool for this activity. Take a look at the tutorial for more details:
http://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/
If you need more help, do ask.
I am assuming that it will return within the body of the ResponseEntity.
Try:
String body = dres.getBody();
You can use something that can parse that string to a json object. Something like:
JSONObject jObject = new JSONObject(body);
See:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
Java String to JSON conversion

Categories

Resources