Json can not deserialize instance Error in Spring - java

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()

Related

Spring RestController with java validation API. Too verbose error messages

I have to write a Rest Controller using Spring.
#PostMapping(value = "/mycontroller", produces = "application/json")
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
public MyDTOOuptup myMethod(#Valid #RequestBody MyDTO input) {
... body ...
}
I wrote a DTO input object that it represents the Request Body of my controller.
Into the DTO I added some validation rules to validate the input before to analize the request into the controller.
#Data
#AllArgsConstructor
#NoArgsConstructor
public class MyDTO {
#NotNull(message="my custom error message for field_a")
#JsonProperty("field_A")
private String fieldA;
#NotNull(message="my custom error message for field_b")
#JsonProperty("field_B")
private String fieldB;
}
It works fine. In case of wrong input I receive 400 - Bad request and appropriate errors description into the response body.
But, I saw that this json body is too verbose.
{
"timestamp": "2020-03-31T14:29:42.401+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotNull.myDTO.field_a",
"NotNull.field_a",
"NotNull.java.lang.String",
"NotNull"
],
"arguments": [
{
"codes": [
"myDTO.field_a",
"field_a"
],
"arguments": null,
"defaultMessage": "field_a",
"code": "field_a"
}
],
"defaultMessage": "my custom error message for field_a",
"objectName": "myDTO",
"field": "productId",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
}
],
"message": "Validation failed for object='myDTO'. Error count: 1",
"path": "/mycontroller"
}
How can I specify that I need only the error description messages or something like this?
Is there a smart/slim structure?
You can define your own custom exception and add Exception handler,
class ExceptionResponse {
private boolean success = false;
private String errorCode;
private String errorMessage;
private String exception;
private List<String> errors;
private String path;
private String timestamp = LocalDateTime.now().toString();
}
Exception handler,
#ControllerAdvice
public class CustomExceptionHandler {
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ExceptionResponse> invalidInput(MethodArgumentNotValidException ex, HttpServletRequest request) {
ExceptionResponse response = getExceptionResponse(); //generate exception response
return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_JSON_UTF8).body(response);
}
}
I hope it helps!!
These attributes are available at DefaultErrorAttributes as a Map<String, Object>.
Default implementation of ErrorAttributes. Provides the following attributes when possible: ...
If you want to modify errors you have to first get the map with the attributes and then modify and finally return it. Attributes are get using the DefaultErrorAttributes::getErrorAttributes method. The removal from the map is fairly simple with Map::remove.
Here is the code that should work. The returned bean should be ErrorAttributes.
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
errorAttributes.remove("errors");
return errorAttributes;
}
};
}

Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token

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.

Unable to use custom HttpMessageNotReadableException error message in Spring Boot

I'm currently trying to provide custom messages for exceptions, but ran into an issue with HttpMessageNotReadableException.
I have an ErrorDetails class:
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
I also have a custom exception handler:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
#RestController
public class CustomizedExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(HttpMessageNotReadableException.class)
#Override
public final ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
ErrorDetails errorDetails = new ErrorDetails(new Date(), "hello",request.getDescription(true));
errorDetails.setMessage("Testing message");
return new ResponseEntity<>(errorDetails,HttpStatus.NOT_ACCEPTABLE);
}
}
But when i try to post a bad request, for example, with a field that should have a integer value I pass a string in the JSON it still returns the default error message of:
{
"timestamp": "2019-03-12T00:15:14.210+0000",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Cannot deserialize value of type `int` from String \"lala\": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `int` from String \"lala\": not a valid Integer value\n at [Source: (PushbackInputStream); line: 5, column: 17] (through reference chain: com.tdl.model.ToDoNote[\"priority\"])",
"path": "/todos"
}
The JSON request:
{
"name": "An workout",
"dateToComplete": "Today",
"description": "Sleep Day",
"priority": "lala",
"completed": false
}
The desired effect would just be the test message appearing instead of the long description.
I also get this in my Eclipse console:
WARN 16508 --- [nio-5000-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type int from String "lala": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type int from String "lala": not a valid Integer value
at [Source: (PushbackInputStream); line: 5, column: 17] (through reference chain: com.tdl.model.ToDoNote["priority"])]
I changed the status to NOT_ACCEPTABLE just to see more clearly if my custom error is returned.
Any help would be appreciated. Thank you.
EDIT
Added ExceptionHandler for InvalidFormatException, but nothing changed. I still get the default error(exception) message same as before.
#ExceptionHandler(InvalidFormatException.class)
public final ResponseEntity<Object> handleInvalidFormat(InvalidFormatException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
ErrorDetails errorDetails = new ErrorDetails(new Date(), "hello",request.getDescription(true));
errorDetails.setMessage("Testing message");
return new ResponseEntity<>(errorDetails,HttpStatus.NOT_ACCEPTABLE);
}
I ran into this error HttpMessageNotReadableException and I felt the need of customizing it. After a few trials, I ended up with a better and more readable format.
Step 1: Create a Custom Error Details class with the fields that you would want to expose to the client. Below is what I created.
public class ErrorDetails {
private final Date timestamp;
private final String message;
private final String details;
public ErrorDetails(Date timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details=details;
}
// getters not included for brevity
Step 2: Create a class that will extend the ResponseEntityHandler which has the exceptions that can be overridden. Here, override the handleHttpMessageNotReadbale method, and then in the method have an implementation of your own custom error handler.
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
Step 3: Run your POST or PUT method with the wrong input fields and check the result. For instance, gender is an enum class with only FEMALE and MALE.
{
"firstName":"Dell",
"lastName":"HP",
"birthYear":"2000-02-12",
"email":"dell#gmail.com",
"gender":"BOY"
}
The response is like below:
{
"timestamp": "2022-06-06T08:08:53.906+00:00",
"message": "JSON parse error: Cannot deserialize value of type com.io.clinic.utils.Gender from String "BOY": not one of the values accepted for Enum class: [FEMALE, MALE]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type com.io.clinic.utils.Gender from String "BOY": not one of the values accepted for Enum class: [FEMALE, MALE]\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 6, column: 14] (through reference chain: com.io.clinic.payloadDTO.PatientDTO["gender"])",
"details": "uri=/api/v1/patients"
}
I was satisfied with having the message in that state for debugging but you can also customize the message response in the overridden method.
The problem is solved. I had my custom exception classes in a badly named package. It was called just exception. While it should have been com.app.exception where the whole project is.

Cannot deserialize instance of int[] out of START_OBJECT token

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) {}

springboot Can not deserialize-HttpMessageNotReadableException

I am trying to read json in my spring boot project.
My JSON data is as follows:
[{
"userId":"101"
},
{
"partNum":"aaa"
},
{
"partNum":"bbb"
},
{
"partNum":"ccc"
}]
I have created a DTO class:
public class TcPartDto {
private String userId;
private List<String> partNum;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public List<String> getPartNum() {
return partNum;
}
}
And I am calling it in my Controller as follows:
#RequestMapping(value = "/volumeinfo", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody
public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto partList) throws Exception {
return tcService.fetchVolumeInfo(partList);
}
But I get the following error:
Through Postman I get this error:
"Could not read document: Can not deserialize instance of
tc.service.model.TcPartDto out of START_ARRAY token\n at [Source:
java.io.PushbackInputStream#5359141a; line: 1, column: 1]; nested
exception is com.fasterxml.jackson.databind.JsonMappingException: Can
not deserialize instance of tc.service.model.TcPartDto out of
START_ARRAY token\n at [Source: java.io.PushbackInputStream#5359141a;
line: 1, column: 1]"
What wrong am I doing?
The DTO you've created does not match the json data it's trying to read.
Based on your DTO sample json should be:
{
"userId" : "someId",
"partNum" : [ "partNum1", "partNum2"]
}
otherwise if json object you're consuming is fixed then DTO should be:
public class MyDTO {
private String userId;
private String partNum;
// ...
}
and with your controller with a parameter of type
List<MyDTO>
You are sending a JSON Array to your public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto partList) method. But it should be deserialize to a single object: TcPartDto partList.
Change your JSON structure to send only a single TcPartDto or make sure your that your volumeinfo method can receive an Array or List.
And you have to change your JSON structure in case you want to send a single object:
{
"userId": 101,
"partNum": [
"aaa",
"bbb",
"ccc"
]
}
As others already pointed out various answers.
if in case this is the json that you want to map without changing the class :
JSON:
[{
"userId":"101"
},
{
"partNum":"aaa"
},
{
"partNum":"bbb"
},
{
"partNum":"ccc"
}]
Class:
#JsonIgnoreProperties(ignoreUnknown=true)
public class TcPartDto {
private String userId;
private List<String> partNum;
//getters and setters
}
Controller:
#RequestMapping(value = "/volumeinfo", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody
public List<TcPartVolumeDto> volumeinfo(#RequestBody TcPartDto[] partArray) throws Exception {
return tcService.fetchVolumeInfo(partArray);
}
Output:
[{"userId":"101","partNum":null},{"userId":null,"partNum":["aaa"]},{"userId":null,"partNum":["bbb"]},{"userId":null,"partNum":["ccc"]}]

Categories

Resources