Springdoc webflux automatically convert String to java.time.LocalDate error - springdoc

When I use org.springdoc:springdoc-openapi-spring-boot-2-webflux:3.1.5to build reactive api.
I got following error:
Caused by: java.time.format.DateTimeParseException: Text '2021-03-24' could not be parsed at index 4
Sample Code:
Controller
#RestController
public class Controller {
#GetMapping(path = "/test")
public Mono<String> test(#Valid TaskCriteria taskCriteria) {
Mono.just("Subscribe completed, criteria: " + taskCriteria.toString());
}
}
Model
#Data
#AllArgsConstructor
#Schema(description = "Task matching criteria", name = "TaskCriteria")
#ParameterObject
public class TaskCriteria {
#Parameter(description = "Date to run, using ISO-8601 format yyyy-MM-dd")
#Schema(required = true, type = "string", format = "date", example = "2021-03-25")
#NotNull
private LocalDate date;
}
Springboot version: 2.3.2.RELEASE

Your problem is that you are using #AllArgsConstructor instead of #NoArgsConstructor, for jackson!
This is your working sample:
#RestController
public class HelloController {
#GetMapping(path = "/test")
public Mono<String> test(#Valid TaskCriteria taskCriteria) {
return Mono.just("Subscribe completed, criteria: " + taskCriteria.toString());
}
#Data
#NoArgsConstructor
#Schema(description = "Task matching criteria", name = "TaskCriteria")
#ParameterObject
public class TaskCriteria {
#Parameter(description = "Date to run, using ISO-8601 format yyyy-MM-dd")
#Schema(required = true, type = "string", format = "date", example = "2021-03-25")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#NotNull
private LocalDate date;
}
}

Related

JSON data type not validated in spring boot

I have json like below
"name": {
"title": "Mr",
"firstName": "somename",
"middleName": "middleName",
"lastName": "Micheal",
"maidenName": "maidenName"
}
My POJO class is defined as like below
#JsonInclude(JsonInclude.Include.NON_NULL)
#Data
#Validated
public class Name {
#Length(min = 0,max = 10)
private String title;
#Length(min = 0,max = 50)
private String firstName;
#Length(min = 0,max = 700)
private String middleName;
#Length(min = 0,max = 50)
private String lastName;
#Length(min = 0,max = 50)
private String maidenName;
}
Controller class
#RestController
#RequestMapping("/price/v1")
#Validated
public class MyServerController implements PriceAPI {
#PostMapping(value = "/price",
produces = {"application/json"},
consumes = {"application/json"})
public ResponseEntity<SomeResponse> getmyResponse
(#Valid
#RequestBody Name name) {
return ResponseEntity.ok(myService.getmyResponse(priceData));
}
}
Issue is when I provide integer to my json first name field it is not throwing any error. example
"name": {
"title": "Mr",
"firstName": 123,
"middleName": "middleName",
"lastName": "Micheal",
"maidenName": "maidenName"
}
I tried few options in jackson and explored few Json annotation. But those are not working as expected. Please help me on this.
You will need to include a pattern for the individual field by defining a regular expression with the #Pattern annotation. In this case, for the firstName field, the appropriate #Pattern should be defined as --
#Length(min = 0, max = 50)
#Pattern(regexp = "^[A-Za-z]+$")
private String firstName;
Include a suitable pattern for all the other fields as required.

Jackson ObjectMapper not reading JSON to POJO

I have the following java object:
Record.class
{
long version;
String data;
String source;
}
I am trying to use the data field and convert it to a Java class using object mapper.
data = "{
\"myUuid\": \"af34b6ab-bebc-443b-af5c-53495905cb0b\",
\"location\": \"UK\",
\"clientName\": \"My_ClientName\",
\"status\": \"SUCCESS\",
\"activeDays\": 251
}";
My corresponding Java class:
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ClientInfo implements TargetEntity {
#JsonAlias("clientName")
#JsonProperty("clientAccountId")
String clientName;
#JsonAlias("location")
#JsonProperty("geo")
String geo;
#JsonAlias("status")
#JsonProperty("clientStatus")
String status;
#JsonAlias("activeDays")
#JsonProperty("numberOfDaysActive")
int activeDays;
#Override
public String getEntityName() {
return "ClientInfo";
}
#Override public boolean isActiveForAtleast1Day() {
return activeDays>0;
}
}
Finally my code which I use:
protected void buildClientInfo(Record clientRecord) {
String data = crmRecord.getData();
ObjectMapper objectMapper = new ObjectMapper();
ClientInfo entityData = objectMapper.readValue(data, ClientInfo.class);
}
My output is always and ObjectMapper is not able to read other values.
{
"activeDays" : 0
}
Using jackson-databind-2.9.x
What am I missing?
Weirdly - if I do new Gson().fromJson(clientRecord.getData(), clazz) I get the output I need except for the isActiveForAtleast1Day.
Posting this code just for reference:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ClientInfo implements TargetEntity {
#JsonAlias("clientName")
#JsonProperty("clientAccountId")
String clientName;
#JsonAlias("location")
#JsonProperty("geo")
String geo;
#JsonAlias("status")
#JsonProperty("clientStatus")
String status;
#JsonAlias("activeDays")
#JsonProperty("numberOfDaysActive")
int activeDays;
#Override
public String getEntityName() {
return "ClientInfo";
}
#Override
public boolean isActiveForAtleast1Day() {
return activeDays>0;
}
// getters/setters
}
// assumption
public interface TargetEntity {
String getEntityName();
boolean isActiveForAtleast1Day();
}
Test code:
String data = "{\"myUuid\": \"af34b6ab-bebc-443b-af5c-53495905cb0b\",\"location\": \"UK\",\"clientName\": \"My_ClientName\",\"status\": \"SUCCESS\",\"activeDays\": 251}";
ObjectMapper objectMapper = new ObjectMapper();
ClientInfo entityData = objectMapper.readValue(data, ClientInfo.class);
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(entityData));
Output:
{
"entityName" : "ClientInfo",
"activeForAtleast1Day" : true,
"clientAccountId" : "My_ClientName",
"geo" : "UK",
"clientStatus" : "SUCCESS",
"numberOfDaysActive" : 251
}

Spring sends empty JSON despite of object being not null

In my controller I have the following method:
#RequestMapping(value = "/getAll", method = RequestMethod.GET)
public List<Topic> getAllTopics() {
List<Topic> allTopics = service.getAllTopics();
assert allTopics.size() > 0; // is not empty
System.out.println(allTopics.get(0)); // Topic{id=1, name='bla', description='blahhh'}
return allTopics;
}
When I go to http://localhost:8080/getAll I get [{},{},{},{}] as a result but service.getAllTopics() returns non empty List So the list to be send is not empty but the browser receives invalid JSON. However, there is no problem in serializing objects since the following method return valid JSON. What's the problem?
#GetMapping("/json")
public List<Locale> getLocales() {
return Arrays.asList(DateFormat.getAvailableLocales());
}
I'm running latest Spring Boot, i.e. 2.1.3.RELEASE.
Update
Here's my entity class - Topic
#Entity
#Table(name = "topic", schema="tetra")
public class Topic {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String description;
public Topic() {
}
public Topic(String name, String description) {
this.name = name;
this.description = description;
}
#Override
public String toString() {
return "Topic{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}
By default , Jackson will only serialise the public fields and public getters into JSON. As the Topic neither have public fields nor the public getter , nothing will be serialised and you get an empty JSON object.
There are plenty of ways to configure it such as:
(1) Simply add public getter for all fields
(2) Use #JsonAutoDetect(fieldVisibility = Visibility.ANY) such that private fields can also be auto detected :
#Entity
#Table(name = "topic", schema="tetra")
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class Topic {
}
(3) Use #JsonProperty to explicitly pick what fields/getter to be serialised .The nice things of this approach is that the field name in JSON can be different from the POJO :
#Entity
#Table(name = "topic", schema="tetra")
public class Topic {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
#JsonProperty("description")
private String description;
}

How to get JSON Object, REST API, Jhipster, SpringBoot

I'm coding an API using Jhipster. Server side is programmed with Spring-Boot. I want to get JSON Object that i send with PostMan
{
"user" : {
"name" : "name",
"surname": "surname"
}
}
I create a ressource and a class to get this JSON Object
#RequestMapping(value = "/",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<AlertBalance> create(#RequestBody User user) throws URISyntaxException {
System.out.println("name "+ user.getName()+"/ surname : "+User.getSurname());
}
User class Code
#JsonIgnoreProperties(ignoreUnknown = true)
public class User implements Serializable {
private String name;
private String surname ;
#JsonCreator
public User(#JsonProperty("surname") String surname , #JsonProperty("name") String name){
this.surname = surname;
this.name = name;
}
public User(){
}
//setters and getters
}
The create() method is called when I do request from postman, but the value of name and surname is null. Do you know what I can do to get the real values ?
I just find the mistake, the JSON Object I sent was incompatible. I change it with
{
"name" : "name",
"surname": "surname"
}
and now it works.

jsr 303 validated all fields in object

suppose i hava two view files(jsps) and one class(BudgetControlRegisterDto)
BudgetControlRegisterDto
public class BudgetControlRegisterDto implements Serializable {
#NotNull(message = "{NotNull.java.util.Date}")
private Date demandReceiveDate;
#NotNull(message = "{NotNull.java.util.Date}")
private Date demandOriginalDate;
#NotNull(message = "Start date {NotNull.java.util.Date}")
private Date startDate;
#NotNull(message = "End date {NotNull.java.util.Date}")
private Date endDate;
// setter and getter
}
In one view file i want to validate startDate and endDate
and in other view file i want to validate demandOriginalDate and demandReceiveDate using json ajax. when validation occurs i get validation message for all fields with below code:
controller class's method this is testing code used by both view files(jsps)
#RequestMapping(value = "/addnewdemand.json", method = RequestMethod.POST)
public #ResponseBody BudgetControlRegisterDto addNewDemand(#Valid #ModelAttribute("bcrDto") BudgetControlRegisterDto bcrDto,Errors errors){
log.info("addNewDemand invoked!");
if(errors.hasErrors()) {
log.info("has errors");
bcrDto.setFieldsErrors(errors.getFieldErrors());
return bcrDto;
}
return bcrDto;
}
.js file this is testing code used by both view files(jsps) below code is ajax response code
if(response.fieldsErrors != null) {
html ='<div class="ui-message-error">';
for(var i= 0; i<response.fieldsErrors.length; i++) {
html+='<span>'+response.fieldsErrors[i].defaultMessage+'</span><br/>';
}
html+='</div>';
$("#bcrForm_message").html(html);
}
Question why m i getting validation message of all fields
this is the situation where groups takes place. Here is a good tutorial about them.
The first thing you need to change is to add groups attribute to your #NotNull annotations.
public class BudgetControlRegisterDto implements Serializable {
#NotNull(message = "{NotNull.java.util.Date}",groups={First.class})
private Date demandReceiveDate;
#NotNull(message = "{NotNull.java.util.Date}",groups={First.class})
private Date demandOriginalDate;
#NotNull(message = "Start date {NotNull.java.util.Date}",groups={Second.class})
private Date startDate;
#NotNull(message = "End date {NotNull.java.util.Date}",groups={Second.class})
private Date endDate;
public interface First {};
public interface Second {};
// setter and getter
}
the second one is to change from #Valid to #Validated because #Valid doesn't support validation groups.
controller's method:
#RequestMapping(value = "/addnewdemand.json", method = RequestMethod.POST)
public #ResponseBody BudgetControlRegisterDto addNewDemand(#Validated(BudgetControlRegisterDto.First.class) #ModelAttribute("bcrDto") BudgetControlRegisterDto bcrDto,Errors errors){
log.info("addNewDemand invoked!");
if(errors.hasErrors()) {
log.info("has errors");
bcrDto.setFieldsErrors(errors.getFieldErrors());
return bcrDto;
}
return bcrDto;
}