JSON Mapping Exception while calling post method with request body - json

I have a controller with the below mentioned contract ---
#RequestMapping(value="/api/devices/certs",method = RequestMethod.POST,consumes={"application/json","application/xml"})
public String submitCertificate(#RequestBody Certificate certificate){
System.out.println(certificate.getBase64String());
return certificate.getBase64String();
}
Other than this there are two pojo classes --
1)
public class DeviceCertificateRequest implements Serializable {
private static final long serialVersionUID = -4408117936126030294L;
private Certificate certificate;
public Certificate getCertificate() {
return certificate;
}
public void setCertificate(Certificate certificate) {
this.certificate = certificate;
}
#Override
public String toString() {
return "DeviceCertifficateRequest [certificate=" + certificate + "]";
}
}
2)
public class Certificate implements Serializable {
private static final long serialVersionUID = 4044105355620137636L;
private String base64String;
public String getBase64String() {
return base64String;
}
public void setBase64String(String base64String) {
this.base64String = base64String;
}
#Override
public String toString() {
return "Certificate [base64String=" + base64String + "]";
}
}
Now I am using spring boot and have added jackson-data-bind dependency for content negotiation, also I wanted to consume both json as well as xml data as an input and thus mapping it to the POJO file.
but I am not able to attain the desired result, even I am getting below mentioned error in the logs when trying to send across json from a rest client.
Error----
ERROR] 2017-02-07 13:48:45.448 [http-nio-8080-exec-1] ConfigManagerExceptionHandler - exception while accessing url:-http://localhost:8080/api/devices/certserror message:-Could not read document: Can not construct instance of com.lufthansa.configmanager.request.beans.Certificate: no String-argument constructor/factory method to deserialize from String value ('DeviceCertificateRequest')
at [Source: java.io.PushbackInputStream#3c891128; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.lufthansa.configmanager.request.beans.Certificate: no String-argument constructor/factory method to deserialize from String value ('DeviceCertificateRequest')
at [Source: java.io.PushbackInputStream#3c891128; line: 1, column: 1]
Json send across --
"certificate": {
"base64String": "abc"
}
Please also let me know whether it will work properly for xml payload as well, as I want to consume both xml as well as json input

Show us how do You make a request and double check the names of variables.
Check and recheck if You have the correct IMPORTS in the controller, if the Certificate is actually from Your package and not any other.
Add
#JsonInclude(Include.NON_NULL)
class Foo{}
so You won`t have null problems.
Delete for testing the serialVersionUID from certificate.
Try to add #ResponseBody to You consuming controller method.
Try to send
{
"base64String": "abc"
}
without the variable name.

I worked by creating parametrised constructor in the POJO class, seems it jackson data bind requires a parametrised constructor for object creation.
Still have to check for xml input though.

Related

springboot server returns error 400 after post request using postman

whenever I try to send this over postman:
{
"date": "2021-11-05 12:32:32",
"start": "start",
"destination": "destination",
"provider": "provider",
"driver":1,
"vehicule":1
}
i get an error 400, bad request, i'm using both the #restController and #requestBody annotations while also setting the content type to json.
i get this error on the debugger:
2021-11-09 16:57:52.086 WARN 11748 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2021-11-06 12:32:32.0": not a valid representation (error: Failed to parse Date value '2021-11-06 12:32:32.0': Cannot parse date "2021-11-06 12:32:32.0": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2021-11-06 12:32:32.0": not a valid representation (error: Failed to parse Date value '2021-11-06 12:32:32.0': Cannot parse date "2021-11-06 12:32:32.0": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
at [Source: (PushbackInputStream); line: 3, column: 17] (through reference chain: com.siam.HRAssistTool.Entity.Schedule["date"])]
I don't understand how i should fix this what i assume date format related issue
when I remove the time from the json body and only leave the date, I get this error:
2021-11-09 17:34:55.418 WARN 11748 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.siam.HRAssistTool.Entity.Vehicule` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.siam.HRAssistTool.Entity.Vehicule` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1)
at [Source: (PushbackInputStream); line: 8, column: 20] (through reference chain: com.siam.HRAssistTool.Entity.Schedule["vehicule"])]
my schedule entity:
#Entity
public class Schedule implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
private Date date ;
private String Start;
private String destination;
#OneToOne( fetch = FetchType.LAZY)
private Staff driver;
#OneToOne(fetch = FetchType.LAZY)
private Vehicule vehicule;
private String provider;
//constructors, getters and setters
}
my controller :
#RestController
public class ScheduleController {
#Autowired
ScheduleService scheduleService;
#PostMapping(value="/schedule/create")
public #ResponseBody String createSchedule( #RequestBody Schedule schedule) {
System.out.println(schedule.toString());
return scheduleService.addSchedule(schedule);
}
//other crud operation
}
First of all, replace Date with LocalDate, which is part of the new Java Time API. with this you can configure Jackson to handle serialization and deserialization of such a complex type easily. Add the following dependency:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.0</version>
</dependency>
And then configure Jackson accordingly:
#Configuration
public class JacksonConfiguration {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
objectMapper.registerModule(javaTimeModule);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
}
Then, please avoid using Entities in your Controller, either as a response or request type. Instead, use DTOs which are specific representations for your core model Entities.
public class ScheduleCreationDto {
private LocalDate date;
private String Start;
private String destination;
private Long driverId; // I am guessing the ID is a Long
private Long vehiculeId; // I am guessing the ID is a Long
private String provider;
//constructors, getters and setters
}
This should now be used as the request body:
#RestController
public class ScheduleController {
#Autowired
ScheduleService scheduleService;
#PostMapping(value="/schedule/create")
public #ResponseBody String createSchedule(#RequestBody ScheduleCreationDto scheduleCreationDto) {
return scheduleService.addSchedule(schedule);
}
//other crud operation
}
You also need to change ScheduleService so that it creates a Schedule based on ScheduleCreationDto. Most of the properties require a simple mapping, but others (driverId and vehiculeId) requires you to actually get those Entities from the Database using the provided ID. Something similar to the following should be done in your ScheduleService:
#Service
public class ScheduleService {
#Autowired
ScheduleRepository scheduleRepository;
#Autowired
DriverRepository driverRepository;
#Autowired
VehiculeRepository vehiculeRepository;
public String addSchedule(ScheduleCreationDto scheduleCreationDto) {
Optional<Driver> driver = driverRepository.findById(scheduleCreationDto.getDriverId());
Optional<Vehicule> vehicule = vehiculeRepository.findById(scheduleCreationDto.getVehiculeId());
if (driver.isPresent() && vehicule.isPresent()) {
Schedule schedule = new Schedule(scheduleCreationDto.getDate(), scheduleCreationDto.getStart(),
scheduleCreationDto.getDestination(), driver.get(), vehicule.get(), scheduleCreationDto.getProvider());
scheduleRepository.save(schedule);
}
return // whatever String you want to return, you should actually return the created Schedule, but that is a different topic
}
//other crud operation
}
400 error bad request with postman and Spring Boot API, in my case, happend for three reasons:
1.The first, is that the json format for the request is wrong, like sending:
{ key: value }
Or:
{ "key" : "value"
This is clarely not you case.
2.The second cause was sending the keys different from what the object was expecting.For example:
#PostMapping
public ResponseEntity<Object> save(#RequestResponse #Valid
ClassOfReciveObject reciveObject){
return ResponseEntity.status(HttpStatus.CREATED).body("OK");
}
If the ClassOfObjectRecived has properties :
{
public String age;
public String name;
}
And you are sending in postman others keys, you will get a bad Request
{
"country":"Brazil",
"Continent":"America"
}
3.The third case i got this error was because of the private access modifier for the atributes of this class, change it for public, or find ways to resolve it
public class ClassOfObjectRecived {
public String param1;
public String param2;
}

JSON parse error: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists)

I have a Spring Boot application using version 2.2.4 and Zulu Java 11.0.5 from Azul. It is accessing a REST web service which is deployed on a Payara web server (version 5.194).
I am using the following DTOs:
public class IdDTO extends BasicResponseDTO {
private long id;
public IdDTO(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and
public class BasicResponseDTO implements Serializable {
private String errorCode;
public BasicResponseDTO() {
this.setErrorCode(null);
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
I invoke a REST web service and from Postman I see I receive (correctly) the following response:
{
"errorCode": null,
"id": 3534016
}
However, when I retrieve the response, I get the following exception:
class org.springframework.web.client.RestClientException/Error while extracting response for type [class com.dto.IdDTO] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.dto.IdDTO` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
Does anyone have any idea on why the application is not able to map the received JSON to the object?
P.S. 1) I also have other DTOs that extend BasicResponseDTO and the de-serialization works fine for them.
P.S. 2) The definition of the classes is the same on both the server and the client.
There is no default constructor on IdDTO. Only one that takes id:
public IdDTO(long id) {
this.id = id;
}
You have to add one:
public IdDTO() {
}
This is needed by JSON deserialization to construct objects from your classes
This has already been answered here: JSON parse error: Can not construct instance of io.starter.topic.Topic
In general design wise, I would strongly recommend going for the second solution with annotating the constructor, rather than a default argument constructor. That way you can have immutable fields (make them final) and ensure that you don't accept nulls. In 2020 I would consider any design using a default constructor due to serialization legacy-design.

Spring boot / Jackson deserializes JSON of wrong type

I'm a litte bit lost, I have to admit. I wrote a Spring Boot (1.3M2) application that receives a JSON object which it needs to store in a database:
#RequestMapping(value = "/fav", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<String> setFavorite(#RequestBody List<Favorite> favorites) {
...
internally this method passes the JSON to another method which stores it line by line in a database:
jdbcTemplate.batchUpdate(INSERT_FAVORITE, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Favorit fav = favorites.get(i);
ps.setString( ...
}
#Override
public int getBatchSize() {
int size = favorites.size();
return size;
}
When I POST a JSON to the controller which does not match the structure of my Favorite-object I only see null values in my database. Obviously Jackson tries its best to convert my JSON into a Java object but fails and sets all values of the object it finds no value for to null.
Then this list of sort of empty objects is written to the database.
I use curl to POST the values
curl -vX POST https://localhost/fav -d #incorrectype.json
This can't be the source of error because it works with a favorite.json. How can I have my controller / Jackson detect if I use a JSON that does not match ?
One solution is to use annotations from javax.validation, and instead of accepting a List in the controller signature, use a custom wrapper along the lines of this (getters/setters omitted):
public class FavoriteList {
#Valid
#NotNull
#Size(min = 1)
private List<Favorite> favorites;
}
then for the Favorite class add the validation as needed, e. g.:
public class Favorite {
#NotNull
private String id;
}
with these changes in place, modify the controller method signature along these lines:
public ResponseEntity<String> setFavorite(#Valid #RequestBody FavoriteList favoritesList) {
This way, input failing validation will throw exceptions before anything in the controller method is executed.

JAX-RS - JSON List to Object with JaxB

I am using JAX-RS (CXF) with JaxB and Jackson to provide a REST-API.
Unfortunately, none of the found results helps me with following (simple) problem:
I implemented following method:
#POST
#Path(ApiStatics.ARMY_CREATE_ARMY)
public com.empires.web.dto.Army createArmy(#FormParam("locationid") long locationId, #FormParam("name") String name, #FormParam("troops") ArmyTroops troops) {
and here are is my model class:
#XmlRootElement
#XmlSeeAlso(ArmyTroop.class)
public class ArmyTroops {
public ArmyTroops() {
}
public ArmyTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
#XmlElement(name = "troops")
private List<ArmyTroop> troops = new ArrayList<ArmyTroop>();
public List<ArmyTroop> getTroops() {
return troops;
}
public void setTroops(List<ArmyTroop> troops) {
this.troops = troops;
}
}
ArmyTroop
#XmlRootElement(name = "troops")
public class ArmyTroop {
#XmlElement
private long troopId;
#XmlElement
private String amount;
public long getTroopId() {
return troopId;
}
public void setTroopId(long troopId) {
this.troopId = troopId;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
}
My json that i send looks like this:
locationid 1
name asdasd
troops {"troops":[{"troopId":4,"amount":"5"},{"troopId":6,"amount":"5"}]}
Unfortunately, the object gets not transformed. Instead I receive this error:
InjectionUtils #reportServerError - Parameter Class com.empires.web.dto.in.ArmyTroops has no constructor with single String parameter, static valueOf(String) or fromString(String) methods
If I provide the constructor with a single string parameter, I get passed the whole json string for "troops" as mentioned above.
Any ideas why JaxB does not work at this point?
You are passing all your parameters with #Form annotation.
But the Form part of the http message must be an xml data structure.
Your 3 parameters don't have a main xml datastructure so it wont work.
In short, form params are send as body.
Cxf use the MultivaluedMap to send params (cxf have an xml model for this structure).
As you can see it is not fit for parameters that can't be trivally serialized.
Here me solution would be to drop the #FormParam to avoid the problem:
1) Use #PathParam #CookieParam to send yours first 2 parameters, and the 'no tag' (body) only for the army compositions.
2) Define an uber object that take all parameters and can be serialized as xml datastructure and use the 'no tag' (body) sending.
3) Use soap, with cxf it is really easy to gets both Rest and Soap.

Parse unnamed mappings in JSON using Jackson

I have some JSON in the following format that I'm trying to parse with Jackson -
"response":{
"response_inner":{
"a":{"field1":2,"field2":0,"field3":5,"field4":0,"field5":[{"field5_1":"b","field5_2":1},{"field5_1":"c","field5_2":1}]},
"d":{"field1":2,"field2":6,"field3":11,"field4":0,"field5":[{"field5_1":"c","field5_2":1},{"field5_1":"b","field5_2":1}]},
"response_inner_bool":false
}
}
Here "a", "b" etc. are some Strings that can change in each response.
I've created a Java object to represent the 'response_inner' (let's call it ResponseInner) and another to represent the object containing the field?s (let's call this one FieldInfo) but I'm not sure how to parse this using the #JsonCreator and #JsonProperty annotations - ResponseInner objects can contain any number of String -> FieldInfo mappings.
I tried parsing it like this -
public class Response {
private ResponseInner responseInner;
#JsonCreator
public Response(#JsonProperty("response_inner") ResponseInner responseInner) {
this.reponseInner = responseInner;
}
}
public class ResponseInner {
private Map<String, FieldInfo> stringToFieldInfoMap;
private boolean responseInnerBool;
#JsonCreator
public ResponseInner(Map<String, FieldInfo> stringToFieldInfoMap, #JsonProperty("response_inner_bool") boolean responseInnerBool ) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.responseInnerBool = responseInnerBool;
}
}
But it complains that Argument #0 of constructor has no property name annotation; must have name when multiple-paramater constructor annotated as Creator. Any suggestions for how to get around this?
You don't seem to be using the stringToFieldInfoMap within ResponseInner anyway. Why do you need to pass it as parameter?
If you do need it in that class, you can simply set it via a setter rather than passing it to constructor.
Alternatively, you could perhaps utilize a third class which deals with that actual mapping of the response, which consumes the Response object (which would in turn consume the ResponseInner object which has had the Map removed from it). This would actually allow you to decouple the mapping logic from the response logic perhaps.
public class MappedResponse {
private Map<String, FieldInfo> stringToFieldInfoMap;
private Response response;
public MappedResponse(Map<String, FieldInfo> stringToFieldInfoMap, Response response) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.response = response;
}
}