Convert String to DTO in the validator of constraint - spring

I have this field in a DTO:
private List<RoleDTO> roles;
It has a validator:
public class InternalUserValidator implements ConstraintValidator<InternalUserConstraint, Object> {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
private String[] rolesAsString;
private List<RoleDTO> roles;
.
.
.
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
//here i get as string, for example: RoleDTO{id=1, name='ADMIN'}
rolesAsString = BeanUtils.getArrayProperty(value, rolesFieldName);//should i use also getproperty again?
//then i try to convert to arraylist:
roles = (List<RoleDTO>) mapper.convertValue(rolesAsString, RoleDTO.class);
but it gives
Cannot construct instance of model.RoleDTO (out of START_ARRAY token
at [Source: UNKNOWN; line: -1, column: -1]
So, while in debug, i also tried this:
(List<RoleDTO>) mapper.convertValue("{\"id\":5,\"name\":\"sad\"}", RoleDTO.class)
This time:
Cannot construct instance of model.RoleDTO (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"name":"sad"}')
at [Source: UNKNOWN; line: -1, column: -1]
What can i do?
This is RoleDTO:
public class RoleDTO implements Serializable {
private Long id;
private String name;
//getters setters
public RoleDTO() {
}

Cannot construct instance of model.RoleDTO (out of START_ARRAY token at [Source: UNKNOWN; line: -1, column: -1]
This Error Means you are trying to deserialize JSONArray to the RoleDto object.. Which is not possible.
The rolesAsString here has value of something like "[{\"id\":5,\"name\":\"sad\"}]"
Use ObjectMapper with TypeReference like this..
mapper.readValue(rolesAsString, new TypeReference<List<RoleDTO>>() {
});

Related

JSON parse error: Cannot deserialize value of type `java.lang.Integer` from Object value (token `JsonToken.START_OBJECT`)

How can I solve the error presented?
this is my service :
#Service
public class EmisionPesoService {
#Autowired
private RestTemplate restTemplate;
private final String URL = "https://desa-dapinstituciones.bancoestado.cl/api-gtw-emision-pesos/dap-min-pub/emision-pesos";
public EmisionPesosDto postEmisionPesos(EmisionPesosBody emisionPesosBody, String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + accessToken);
HttpEntity<EmisionPesosBody> entity = new HttpEntity<>(emisionPesosBody, headers);
ResponseEntity<EmisionPesosDto> response = restTemplate.exchange(URL, HttpMethod.POST, entity, EmisionPesosDto.class);
return response.getBody();
}
}
this is my controller:
#RestController
public class EmisionPesoController {
#Autowired
private EmisionPesoService emisionPesoService;
#PostMapping(value = "/emision-pesos", consumes = "application/json", produces = "application/json")
public EmisionPesosDto emisionPesos(#RequestHeader("Authorization") String accessToken,
#RequestBody Integer CodigoMoneda,
#RequestBody Integer Plazo,
#RequestBody BigDecimal MontoDeposito,
#RequestBody String IndicadorRenovacion,
#RequestBody Integer RutCliente,
#RequestBody String DigitoRutCliente,
#RequestBody Long NumCuentaCorriente,
#RequestBody String RUC,
#RequestBody String RUE,
#RequestBody String FiscaliaRegional,
#RequestBody String FiscaliaZonal,
#RequestBody Integer RutUsuarioEmisor,
#RequestBody String DigitoRutUsuarioEmisor) {
EmisionPesosBody emisionPesosBody = new EmisionPesosBody();
emisionPesosBody.setCodigoMoneda(CodigoMoneda);
emisionPesosBody.setPlazo(Plazo);
emisionPesosBody.setMontoDeposito(MontoDeposito);
emisionPesosBody.setIndicadorRenovacion(IndicadorRenovacion);
emisionPesosBody.setRutCliente(RutCliente);
emisionPesosBody.setDigitoRutCliente(DigitoRutCliente);
emisionPesosBody.setNumCuentaCorriente(NumCuentaCorriente);
emisionPesosBody.setRUC(RUC);
emisionPesosBody.setRUE(RUE);
emisionPesosBody.setFiscaliaRegional(FiscaliaRegional);
emisionPesosBody.setFiscaliaZonal(FiscaliaZonal);
emisionPesosBody.setRutUsuarioEmisor(RutUsuarioEmisor);
emisionPesosBody.setDigitoRutUsuarioEmisor(DigitoRutUsuarioEmisor);
return emisionPesoService.postEmisionPesos(emisionPesosBody , accessToken);
}
}
And the corresponding data
#Data
public class EmisionPesosBody {
private Integer CodigoMoneda;
private Integer Plazo;
private BigDecimal MontoDeposito;
private String IndicadorRenovacion;
private Integer RutCliente;
private String DigitoRutCliente;
private Long NumCuentaCorriente;
private String RUC;
private String RUE;
private String FiscaliaRegional;
private String FiscaliaZonal;
private Integer RutUsuarioEmisor;
private String DigitoRutUsuarioEmisor;
}
#Data
public class EmisionPesosDto {
private int code;
private String message;
private EmisionPesosPayloadDto payload;
}
#Data
public class EmisionPesosPayloadDto {
private String CodProducto;
private String NumOperacion;
private Integer CodigoMoneda;
private String CodSubProducto;
private String Plazo;
private Integer FechaEmision;
private Integer FechaVencimiento;
private BigDecimal Monto;
private BigDecimal MontoOrigen;
private BigDecimal TasaBase;
private BigDecimal TasaPeriodo;
private BigDecimal MontoOrgIntPeriodo;
private BigDecimal MontoFinalOrg;
private String RUC;
private String RUE;
private String FiscaliaRegional;
private String FiscaliaZonal;
}
I am working with rest template and the application requests a client certificate that loads correctly, the endpoints need a header with an access token which I obtain from another endpoint, which works correctly.
My main problem is that when trying to consume this endpoint, which are the resources, it shows me this error:
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.lang.Integer from Object value (token JsonToken.START_OBJECT); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.lang.Integer from Object value (token JsonToken.START_OBJECT) at [Source: (org.springframework. util.StreamUtils$NonClosingInputStream); line: 1, column: 2]]
By leaving my controller like this, it returns a response that there are invalid parameters and returns null
#PostMapping(value = "/emision-pesos", consumes = "application/json", produces = "application/json")
public ResponseEntity<EmisionPesosDto> emisionPesos(#RequestHeader("Authorization") String accessToken,
#RequestBody EmisionPesosBody emisionPesosBody){
EmisionPesosDto response = emisionPesoService.postEmisionPesos(emisionPesosBody, accessToken);
return ResponseEntity.ok(response);
}
{
"code": 400,
"message": "Parámetro inválido",
"payload": {
"fiscaliaRegional": null,
"ruc": null,
"rue": null,
"fiscaliaZonal": null,
"codigoMoneda": null,
"plazo": null,
"codSubProducto": null,
"fechaVencimiento": null,
"montoOrgIntPeriodo": null,
"tasaPeriodo": null,
"numOperacion": null,
"monto": null,
"montoFinalOrg": null,
"codProducto": null,
"fechaEmision": null,
"montoOrigen": null,
"tasaBase": null
}
}
Swagger Documentation:
Request
{
"CodigoMoneda": 999,
"Plazo": 7,
"MontoDeposito": 105000,
"IndicadorRenovacion": "S",
"RutCliente": 61935400,
"DigitoRutCliente": "1",
"NumCuentaCorriente": 23909000386,
"RUC": "000022010212467",
"RUE": "000000012229907",
"FiscaliaRegional": "005",
"FiscaliaZonal": "000500",
"RutUsuarioEmisor": 12878658,
"DigitoRutUsuarioEmisor": "9"
}
Response
{
"CodigoMoneda": 999,
"Plazo": 7,
"MontoDeposito": 105000,
"IndicadorRenovacion": "S",
"RutCliente": 61935400,
"DigitoRutCliente": "1",
"NumCuentaCorriente": 23909000386,
"RUC": "000022010212467",
"RUE": "000000012229907",
"FiscaliaRegional": "005",
"FiscaliaZonal": "000500",
"RutUsuarioEmisor": 12878658,
"DigitoRutUsuarioEmisor": "9"
}
That is what I have in the documentation that the client gave me
The version emisionPesos(#RequestHeader("Authorization") String accessToken, #RequestBody EmisionPesosBody emisionPesosBody) is correct.
The other version with multiple arg annotated as #RequestBody is wrong - only one arg can be #RequestBody. The first arg is an integer so the deserialiser, which gets an object start, ie ”{“ and not an integer, complains.
You will have to check the api definition for desa-dapinstituciones.bancoestado.cl to sort out the missing parameters. Do you have a swagger doc for the POST to api-gtw-emision-pesos/dap-min-pub/emision-pesos.
?

RestTemplate: While calling an API from one localhost to another i get error

I am working on spring boot project. In my eclipse I run two project first one is flight_system and second one is travel-site. I am sending request from travel-site to flight_system for get booking but i am getting error in postman. Url for get booking in flight_system is localhost:8050/booking which is work correctly in postman. Url for get booking in travel-site is localhost:8051/travel-site/booking which produce error in postman.
Here down is my code of flight_system:
Entity
#Entity
public class Booking {
#Id
private String bookingId;
private String passangerName;
private String flightName;
private String source;
private String destination;
// getter setter constructor
}
Controller
#RestController
public class BookingController {
#Autowired
private BookingService bookingService;
#GetMapping("/booking")
public List<Booking> getBooking() {
return bookingService.getAllBooking();
}
}
Here down is my code of travel-site:
Model
public class Booking {
private String bookingId;
private String passangerName;
private String flightName;
private String source;
private String destination;
// getter setter constructor
}
Controller
#RestController
public class TravelSiteController {
#Autowired
private RestTemplate restTemplate;
static final String baseUrl = "http://localhost:8050/";
#GetMapping(value = "/travel-site/booking")
public Booking getBooking() {
ResponseEntity<Booking> responseEntity = restTemplate.exchange(baseUrl + "booking", HttpMethod.GET, null,
Booking.class);
return responseEntity.getBody();
}
}
Stack trace in postman
"message": "Error while extracting response for type [class com.travelsite.model.Booking] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type com.travelsite.model.Booking from Array value (token JsonToken.START_ARRAY); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type com.travelsite.model.Booking from Array value (token JsonToken.START_ARRAY)\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1]"
Try changing
ResponseEntity<Booking> responseEntity = restTemplate.exchange(baseUrl + "booking", HttpMethod.GET, null,
Booking.class);
return responseEntity.getBody();
to
List<Booking> booking = restTemplate
.getForObject(baseUrl + "booking",List.class);
To know better about rest template method you can go through this link

Error when de-serializing enum, com.fasterxml.jackson.databind.exc.InvalidDefinitionException

I have two classes, Incident and Status. Status is an enum
Incident.class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Incident implements Serializable {
#JsonFormat(pattern = "dd/MM/yyyy HH:mm:ss", timezone = "UTC")
private Instant createdDate;
#JsonFormat(pattern = "dd/MM/yyyy HH:mm:ss", timezone = "UTC")
private Instant closeDate;
private Enum<Status> status;
}
Status.class
#AllArgsConstructor
public enum Status {
OPEN("OPEN"),
ON_GOING("ON_GOING"),
CLOSED("CLOSED");
private String status;
}
I tried to de-serialize json payload
#Test
public void deserializingTest() throws JsonProcessingException {
String payloadJson = "{" +
"\"createdDate\":\"08/06/2022 08:08:52\"," +
"\"closeDate\":\"08/06/2022 08:08:52\"," +
"\"status\":\"CLOSED\"" +
"}";
Incident actual = new ObjectMapper()
.registerModule(new JavaTimeModule())
.readValue(payloadJson, Incident.class);
Incident expected = new Incident(
InstantGenerator.generateInstantUTC(2022,6,8, 8, 8, 52),
InstantGenerator.generateInstantUTC(2022,6, 8, 8, 8, 52),
Status.OPEN);
assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
}
And i get following error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.lang.Enum` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"createdDate":"08/06/2022 08:08:52","closeDate":"08/06/2022 08:08:52","status":"CLOSED"}"; line: 1, column: 81] (through reference chain: com.rtambun.dto.incident.Incident["status"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)
I tried to find the same error but seems no one reporting issue on enum not able to find Creator.

Feign client error - Page size must not be less than one

I have the following Rest controller method:
#GetMapping
public Page<CompanyDto> findAllCompanies(#RequestParam(value = "name", required = false) String name, Pageable pageable, JwtAuthenticationToken jwtAuthenticationToken) {
...
and the Feigh client:
#GetMapping
RestPageImpl<CompanyDto> findAllCompanies(#RequestParam(value = "name", required = false) String name, Pageable pageable, #RequestHeader("Authorization") String token);
So far everything works fine.
Now, I'd like to substitute name and pageable parameters with a single DTO object:
public class CompanyRequest {
private CompanyDto company;
Pageable pageable;
public CompanyRequest() {
}
public CompanyRequest(CompanyDto company, Pageable pageable) {
this.company = company;
this.pageable = pageable;
}
public CompanyDto getCompany() {
return company;
}
public Pageable getPageable() {
return pageable;
}
}
to something like this:
controller:
#GetMapping
public Page<CompanyDto> findAllCompanies(CompanyRequest companyRequest, JwtAuthenticationToken jwtAuthenticationToken) {
...
Feign client:
#GetMapping
RestPageImpl<CompanyDto> findAllCompanies(CompanyRequest companyRequest, #RequestHeader("Authorization") String token);
Right now the invocation of the following code:
companyApiClient.findAllCompanies(new CompanyRequest(new CompanyDto("Company1 name", null), PageRequest.of(0, 10)), accessToken);
fails with the following exception:
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!; nested exception is com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!
at [Source: (PushbackInputStream); line: 1, column: 1499]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:389)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105)
... 42 more
Caused by: com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!
at [Source: (PushbackInputStream); line: 1, column: 1499]
at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:47)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1754)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:491)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:514)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:285)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:202)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:490)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:378)
... 44 more
Caused by: java.lang.IllegalArgumentException: Page size must not be less than one!
at org.springframework.data.domain.AbstractPageRequest.<init>(AbstractPageRequest.java:48)
at org.springframework.data.domain.PageRequest.<init>(PageRequest.java:45)
at org.springframework.data.domain.PageRequest.of(PageRequest.java:72)
at org.springframework.data.domain.PageRequest.of(PageRequest.java:60)
at com.decisionwanted.api.model.dto.page.RestPageImpl.<init>(RestPageImpl.java:27)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
... 53 more
What am I doing wrong and how to fix it?
UPDATED
This is RestPageImpl:
public class RestPageImpl<T> extends PageImpl<T> {
#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public RestPageImpl(#JsonProperty("content") List<T> content,
#JsonProperty("number") int number,
#JsonProperty("size") int size,
#JsonProperty("totalElements") Long totalElements,
#JsonProperty("pageable") JsonNode pageable,
#JsonProperty("last") boolean last,
#JsonProperty("totalPages") int totalPages,
#JsonProperty("sort") JsonNode sort,
#JsonProperty("first") boolean first,
#JsonProperty("numberOfElements") int numberOfElements) {
super(content, PageRequest.of(number, size), totalElements);
}
public RestPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestPageImpl(List<T> content) {
super(content);
}
public RestPageImpl() {
super(new ArrayList<>());
}
}

GSON deserialization behaviour

I am deserializing a JSON payload to a POJO and in the POJO I have a HashMap (userIdMap) that is declared & initialized as follows:
public class ObjectProvider {
private String companyId;
#Expose(serialize = true, deserialize = true)
#SerializedName("entity_id")
private String entityId;
#Expose(serialize = true, deserialize = true)
#SerializedName("url")
private String url;
private String responseTemplate;
private Map<String, String> userIdMap = new HashMap<String, String>();
public String getEntityId() {
return entityId;
}
...// REST OF CODE REMOVED FOR READABILITY
}
However after the POJO is created when I try to access the userIdMap attribute to perform a GET operation I get java.lang.NullPointerException. Any ideas as to what could be going wrong ?
The issue has been solved. Looks like the default constructor was missing in the class. Once I add the default constructor and initialize the Map it works fine.

Resources