Here is my Controller mapping for put request:
#PutMapping("/voteForPostByUser")
public String vote(#RequestParam(value = "postId", required =
true) String postId, #RequestParam(value = "userId", required = true)
Integer userId, #RequestBody Boolean vote) {
BlogPostVoteDTO blogPostVoteDTO = new BlogPostVoteDTO
(postId, userId, vote);
return
this.blogPostService.updateBlogPostVotes(blogPostVoteDTO);
}
When I run the following request from POSTMAN:
http://localhost:8082/microblog/api/voteForPostByUser?postId=5d564a2638195729900df9a6&userId=5
Request Body:
{
"vote" : true
}
I get the following exception
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Cannot deserialize instance of
`java.lang.Boolean` out of START_OBJECT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of `java.lang.Boolean` out of START_OBJECT token\n
at [Source: (PushbackInputStream); line: 1, column: 1]",
"trace":
"org.springframework.http.converter.HttpMessageNotReadableException: JSON
parse error: Cannot deserialize instance of `java.lang.Boolean` out of
START_OBJECT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of `java.lang.Boolean` out of START_OBJECT token\n
at [Source: (PushbackInputStream); line: 1, column: 1]\r\n\tat
I is probably something simple, but I don't get what am I missing ?
You just need to send true or false as the body of the request, no need for curly braces or key-value structure
I try on postman using true or false like this.It's ok.
Create a new class for your payload:
class Payload {
Boolean vote;
// maybe some getters/setter here
}
and use it as your RequestBody
#PutMapping("/voteForPostByUser")
public String vote(#RequestParam(value = "postId", required = true) String postId, #RequestParam(value = "userId", required = true) Integer userId, #RequestBody Payload payload) {
boolean vote = payload.vote; //or payload.getVote()
BlogPostVoteDTO blogPostVoteDTO = new BlogPostVoteDTO(postId, userId, vote);
return this.blogPostService.updateBlogPostVotes(blogPostVoteDTO);
}
You expect a boolean from your #RequestBody Boolean vote however JSON sends text. You can either use the Payload class as suggested already but you can also simply change your controller to expect a String like this #RequestBody String vote and convert that string into boolean using Boolean.valueOf(vote) to be able to use it where you need it.
Related
I have a nested json pojo where the nested part of json is tagged with #JsonRawValue. I am trying it to map with rest template, but I am getting the error
JSON parse error: Cannot deserialize instance of java.lang.String out of START_OBJECT token;
The nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException.
This is what my response object looks like:
import com.fasterxml.jackson.annotation.JsonRawValue;
public class ResponseDTO {
private String Id;
private String text;
#JsonRawValue
private String explanation;
//getters and setters;
}
where explanation is a json mapped to a string. This works fine with postman, swagger, and I see the explanation as json in the response.
But when I am testing it using Rest Template:
ResponseEntity<ResponseDTO> resonseEntity = restTemplate.exchange(URI, HttpMethod.POST, requestEntity, ResponseDTO.class);
I see this exception:
org.springframework.web.client.RestClientException: Error while extracting
response for type [class com.**.ResponseDTO] and content type
[application/json;charset=utf-8]; nested exception is
org.springframework.http.converter.HttpMessageNotReadableException: JSON
parse error: Cannot deserialize instance of java.lang.String out of
START_OBJECT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 604] (through
reference chain: com.****.ResponseDTO["explanation"])
Jackson is telling you that it can't insert an Object (in the error log) inside a String.
The #JsonRawValue is used during serialization of objects to JSON format. It is a way to indicate that the String field is to be sent as-is. In other words, the purpose is to tell Jackson that the String is a valid JSON and should be sent without escaping or quoting.
What you can do instead is provide Jackson with a custom method for it to set the field value. Using JsonNode as the argument will force Jackson to pass the "raw" value. From there you can get the string representation:
public class ResponseDTO {
private String Id;
private String text;
private String explanation;
//getters and setters;
#JsonProperty("explanation")
private void unpackExplanation(JsonNode explanation) {
this.explanation = explanation.toString();
}
}
I am trying to send a json long list and take records from db.
My controller is:
#Api(tags = Endpoint.RESOURCE_customer, description = "customer Resource")
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public class CustomerResourceController {
private final customerService customerService;
public CustomerResourceController(customerService customerService) {
this.customerService = customerService;
}
#ApiOperation(
value = "Return customer",
response = customerDto.class, responseContainer="List"
)
#PostMapping(value = Endpoint.RRESOURCE_customer_ID)
public List<customerDto> getCustomersByIds(#RequestBody List<Long> ids) {
return customerService.findcustomerIds(ids);
}
}
and client class is:
#Headers("Content-Type: " + MediaType.APPLICATION_JSON_VALUE)
public interface CustomerClient {
#RequestLine("POST /customer/customers/search")
List<LocGrpDto> getCustomersByIds(#RequestBody #Validated List<Long> ids);
}
And i test this service in postman with JSON:
{ "ids": [1,7,8] }
But I get this error:
{
"timestamp": "2018-10-05T13:29:57.645+0000",
"status": 400,
"error": "Bad Request",
"message": "Could not read document: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token\n at [Source: java.io.PushbackInputStream#3cb8b584; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token\n at [Source: java.io.PushbackInputStream#3cb8b584; line: 1, column: 1]",
"path": "/api/v1/customer/customers/search",
"errors": []
}
What is the problem? Do you see any problem here or it may be caused because of my service class or dto classes ?
Try requesting with the payload [1,7,8], not {"ids": [1,7,8]}.
Your JSON would translate to a request body with the next format.
class Body {
private List<Long> ids;
// constructor, getters and setters
}
For a REST client, you can take a look at RestTemplate.
RestTemplate template;
List<Long> ids;
List<CustomerDto> = template.exchange(
"/customer/customers/search",
HttpMethod.POST,
new HttpEntity<>(ids),
new ParameterizedTypeReference<List<CustomerDto>>() {}).getBody()
Hello guys I want to send Array of int and String as RequestBody:
This is the json:
{
"customUiModel": [1, 3, 5],
"user": "user"
}
This is the endpoint code:
#RequestMapping(value = "/save", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public CustomUiModel createCustomUiObject(#RequestBody #Valid int[] customUiModel, String user) {
return customAppService.saveCustom(customUiModel, user);
}
And this is the error :
"message": "JSON parse error: Cannot deserialize instance ofint[]out
of START_OBJECT token; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance ofint[]out of START_OBJECT token\n at [Source:
(PushbackInputStream); line: 1, column: 1]", "path": "/custom/save"
I have tried with Array instead this int[] but I have got same error...
Create an object instead of int[], String to hold them,
public class Example {
private int[] customUiModel;
private String user;
}
and change controller method to,
public CustomUiModel createCustomUiObject(#RequestBody #Valid Example exe) {}
I wanted to write a small and simple REST service using Spring Boot.
Here is the REST service code:
#Async
#RequestMapping(value = "/getuser", method = POST, consumes = "application/json", produces = "application/json")
public #ResponseBody Record getRecord(#RequestBody Integer userId) {
Record result = null;
// Omitted logic
return result;
}
The JSON object I sent is the following:
{
"userId": 3
}
And here is the exception I got:
WARN 964 --- [ XNIO-2 task-7]
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP
message:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: Can not deserialize instance of
java.lang.Integer out of START_OBJECT token at [Source:
java.io.PushbackInputStream#12e7333c; line: 1, column: 1]; nested
exception is com.fasterxml.jackson.databind.JsonMappingException: Can
not deserialize instance of java.lang.Integer out of START_OBJECT
token at [Source: java.io.PushbackInputStream#12e7333c; line: 1,
column: 1]
Obviously Jackson can not deserialize the passed JSON into an Integer. If you insist to send a JSON representation of a User through the request body, you should encapsulate the userId in another bean like the following:
public class User {
private Integer userId;
// getters and setters
}
Then use that bean as your handler method argument:
#RequestMapping(...)
public #ResponseBody Record getRecord(#RequestBody User user) { ... }
If you don't like the overhead of creating another bean, you could pass the userId as part of Path Variable, e.g. /getuser/15. In order to do that:
#RequestMapping(value = "/getuser/{userId}", method = POST, produces = "application/json")
public #ResponseBody Record getRecord(#PathVariable Integer userId) { ... }
Since you no longer send a JSON in the request body, you should remove that consumes attribute.
Perhaps you are trying to send a request with JSON text in its body from a Postman client or something similar like this:
{
"userId": 3
}
This cannot be deserialized by Jackson since this is not an Integer (it seems to be, but it isn't). An Integer object from java.lang Integer is a little more complex.
For your Postman request to work, simply put (without curly braces { }):
3
I'm trying out the RestTemplate stuff from spring. I'm trying to read in this JSON data: JSON Data. The data is a a key value pair in which the key is "geonames" and the value is an array of "geoname" objects.
I have a Geoname class to handle the input. This class also has getters and setters in it. I then have an app class that just runs a main method to invoke a RestTemplate object:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Geoname {
private String name;
private long lat;
private long lng;
private String countrycode;
}
App.java
public class App
{
public static void main( String[] args )
{
String jsonUrl = "http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo";
RestTemplate template = new RestTemplate();
ResponseEntity<Geoname[]> entity = template.getForEntity(jsonUrl, Geoname[].class);
List<Geoname> data = Arrays.asList(entity.getBody());
System.out.print("Success!");
}
}
This is my error output:
Exception in thread "main" org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:208)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:200)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:812)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:796)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:576)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:529)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:261)
at com.declan.App.main(App.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.declan.Geoname[] out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#54fc3ac5; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:835)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:831)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.handleNonArray(ObjectArrayDeserializer.java:232)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:139)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:17)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2660)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:205)
... 13 more
I've tried this setup, i've tried using getForObject like on the spring documentation, I've tried searching here on stackoverflow and using the common answer of mapping to a list. I've even tried creating a Geonames class that contained just an array or Geoname objects but that didn't work either...Same error all the time. Perhaps, I'm not reading the Json correctly but if someone could lend me a pair of eyes I'd be grateful. :D
Cheers!
EDIT
Okay, I now have this new class and it now makes the GET request for the JSON data. However, after a debug, the ResponseEntity body has the array set to null. Do I need to instantiate the array in Geonames manually?
#JsonIgnoreProperties(ignoreUnknown = true)
public class Geonames {
#JsonProperty("geonames")
Geoname[] geonames;
public void setGeonames(Geonames[] geonames) {
this.geonames = geonames;
}
public void getGeonames() {
return geonames;
}
}
Resolved. Turns out that the null was because the JSON link had a max hits per hour limit. By creating my own account on the site, the api gave me my own limits. So the response body then populated with data.