Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'date' in thymeleaf form - spring

<form th:action="#{/hi}" th:object="${datum}" method="post">
<p>date : <input type="date" th:field="*{date}"/> </p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
Controller for form above
#PostMapping("/hi")
fun testum(#ModelAttribute datum: Datum) {
println(datum)
}
simple pojo class
class Datum(
#DateTimeFormat(pattern = "yyyy-MM-dd")
var date: LocalDate? = null
)
I am trying to send date in form but get this exception:
Resolved exception caused by Handler execution: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'datum' on field 'date': rejected value [2018-06-20]; codes [typeMismatch.datum.date,typeMismatch.date,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [datum.date,date]; arguments []; default message [date]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2018-06-20'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2018-06-20]]
But if I change type from LocalDate to String it works fine. I want to map date that is in form to date property of Datum class. can anyone help me with that? any links? thanks.
This link did not help me similar problem

It's an old question, but I'm leaving an answer just for future readers.
This is not an issue with thymeleaf, and more of an issue of #DateTimeFormat binding.
The example in the question above will work if the Datum class is changed like so:
Solution 1.
class Datum {
#DateTimeFormat(pattern = "yyyy-MM-dd")
var date: LocalDate? = null
}
note that the date field has been moved from declaring in the primary constructor into the class body.
(notice the change from Datum (...) to Datum {...})
Solution 2.
class Datum (
#field:DateTimeFormat(pattern = "yyyy-MM-dd") var date: LocalDate? = null
)
if you need to have it declared inside constructor due to having it as data class or other reasons, you have to annotate using use-site targets.
HOWEVER, beware that Solution 2 may not always work. I cannot reproduce in a sample project - but in a real-life project, there was an issue where #field:DateTimeFormat didn't correctly bind the request parameter string to the Date object. It sometimes worked and sometimes didn't, making it very tricky to debug.
When it didn't work, it spewed out errors like Validation failed for object='Datum'. Error count: 1, while reverting to Solution 1 instead always worked. Every time we compiled again, Solution 2 sometimes worked and randomly broke without any code changes.
We've resorted to strictly putting #DateTimeFormat annotated fields down to the class body declaration to insure it works.

I just created your example using this as controller:
#Controller
public class StackOverflow {
#GetMapping("/stack")
public String template(Model m) {
m.addAttribute("datum", new Datanum());
return "stackoverflow.html";
}
#PostMapping("/stack2")
public String testum(#ModelAttribute Datanum user) {
System.out.println(user.getDate());
return null;
}
}
this as view:
<!DOCTYPE html>
<html xmlns:th="http://www.thymleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form th:action="#{/stack2}" th:object="${datum}" method="post">
<p>date : <input type="date" th:field="*{date}"/> </p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
and this as Bean
import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
public class Datanum{
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
}
and it worked:
The difference I see is that you're using var on your Bean
var date: LocalDate? = null
I think that is Java 10, isn't it? why don't you try to use
the bean as I did , maybe that could help you.
instead of var use LocalDate
hope this works

Related

Form input type DateTime using Thymeleaf + Spring MVC issue

I am struggling to use a Java LocalDateTime scheduledDateTime in a Thymeleaf form and getting it back to save it in the database. I get the following error message :
Bean property 'campaignExecution' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
It is really due to the datetime field. If I remove it from the html form everything works fine and I see the data from the object created in the controller.
My object contains the following along with the getter and setter returning thetype (LocalDateTime) :
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
private LocalDateTime scheduledDateTime;
My Controller initialize the value to now()
#RequestMapping({ "/campaign1"})
public String requestCampaign1(Model model) {
CampaignExecution ce = new CampaignExecution();
LocalDateTime localDateTime = LocalDateTime.now();
ce.setScheduledDateTime(localDateTime);
model.addAttribute("campaignExecution", ce);
And this is the form :
<form id="f" name="f" th:action="#{/campaign1}"
th:object="${campaignExecution}" method="post">
<div>
Schedule a date :
<input type="datetime-local" th:field=*{campaignExecution.scheduledDateTime} />
</div>
<hr>
<div>Parameters</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Much easier than I was thinking. For th:field you cannot use multiple object but only the "form object". I was using the full qualifier : object name.property name.

How pass params to Thymeleaf Ajax Fragment

I have a Spring MVC controller that returns the name of a thymeleaf fragment to view resolver bean. The problem is that this fragment needs a url as a parameter. Here I put the fragment:
<!-- A fragment with wrapper form for basic personal information fragment -->
<th:block th:fragment="form-basic(url)">
<form role="form" th:action="${url}" method="post" th:object="${user}">
<th:block th:replace="admin/fragments/alerts::form-errors"></th:block>
<th:block th:include="this::basic" th:remove="tag"/>
<div class="margiv-top-10">
<input type="submit" class="btn green-haze" value="Save" th:value="#{admin.user.form.save}" />
<input type="reset" class="btn default" value="Reset" th:value="#{admin.user.form.reset}" />
</div>
</form>
</th:block>
I can not get a way to pass that parameter without getting an error.
The controller is as follows:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
logger.info(user.toString());
if(!model.containsAttribute(BINDING_RESULT_NAME)) {
model.addAttribute(ATTRIBUTE_NAME, user);
}
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic({url})";
}
For the above example I get the following error:
06-Jan-2017 11:36:40.264 GRAVE [http-nio-8080-exec-9] org.apache.catalina.core.StandardWrapperValve.invoke El Servlet.service() para el servlet [dispatcher] en el contexto con ruta [/ejercicio3] lanzó la excepción [Request processing failed; nested exception is java.lang.IllegalArgumentException: Invalid template name specification: 'admin/fragments/user/personal::form-basic({url})'] con causa raíz
java.lang.IllegalArgumentException: Invalid template name specification: 'admin/fragments/user/personal::form-basic({url})'
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:275)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:189)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
I have done these tests:
"admin/fragments/user/personal::form-basic('{url}')";
"admin/fragments/user/personal::form-basic(#{/admin/users/self/profile})";
"admin/fragments/user/personal::form-basic(/admin/users/self/profile)";
"admin/fragments/user/personal::form-basic('/admin/users/self/profile')";
In all I get error
You have two ways for pass parameter from controller to Thymeleaf fragment. First is the usual Spring way - throw the model:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic";
}
That's enough. In this case you don't need specify any fragment parameters (even if you have it).
Second way is specify parameters in fragment name:
#RequestMapping(method = RequestMethod.GET)
public String show(#CurrentUser User user, Model model) {
String url = "/admin/users/self/profile";
return String.format("admin/fragments/user/personal::form-basic(url='%s')",url);
}
Note, that name of parameter must be specified, and the string value must be placed in single quotes. In this case you don't need add url variable into model.
All you need to do is include your url parameter as model attribute (as you did). There is no need to notifying your fragment name in any way. Just return fragment name as it would have no parameters.
// rest of method body omitted
model.addAttribute("url", "/admin/users/self/profile");
return "admin/fragments/user/personal::form-basic";
Btw. I see that your url lead to some other endpoint. In that case you should use it in #{...} manner in your th:action attribute of the form.
<form role="form" th:action="#{${url}}" method="post" th:object="${user}">

BindingResult requires dates input even without not null or validation

I got an issue with a web application in Spring.
I am writing a search jsp form with related controller and service.
I do not need some datas to be necessary, and it goes all fine but dates.
It requires me to insert dates into dates input tags, if I leave that boxes empty I got an error in BindingResult and my search service stops.
Why does it not accept empty values?
In the domain the attributes are not set to NotNull, and I even remove the #Valid annotation from the service, but it does continues to ask me for some datas into that fields.
Can anyone try to explain me where should I look to solve this issue?
Here is the code:
jsp form:
<form:form commandName="colloquioRicerca" action="colloquio_ricerca" method="post">
<fieldset>
<legend>Cerca un colloquio</legend>
<p class="errorLine">
<form:errors path="codiceFiscale" cssClass="error"/>
</p>
<p>
<label for="codiceFiscale">Codice Fiscale del Docente: </label>
<form:input id="codiceFiscale" path="codiceFiscale" tabindex="1"/>
</p>
<h5>Ricerca avanzata:</h5>
<p>
<label for="nome">Nome:<br><small>(accetta parziali)</small> </label>
<form:input id="nome" path="nome" tabindex="2"/>
</p>
<p>
<label for="cognome">Cognome:<br><small>(accetta parziali)</small></label>
<form:input id="cognome" path="cognome" tabindex="3"/>
</p>
<p>
<label for="dataColloquio">Cerca per data: </label>
<form:input id="dataColloquio" path="dataColloquio" tabindex="4" placeholder="dd/MM/yyyy"/>
</p>
<p>Cerca per periodo:</p>
<p>
<label for="dataIniz">Dal:</label>
<form:input id="dataIniz" path="dataIniz" tabindex="5" placeholder="dd/MM/yyyy"/>
<label for="dataFin">Al:</label>
<form:input id="dataFin" path="dataFin" tabindex="6" placeholder="dd/MM/yyyy"/>
</p>
<p>
<label for="esitoColloquio">Al:</label>
<form:input id="esitoColloquio" path="esitoColloquio" tabindex="7" placeholder="dd/MM/yyyy"/>
<!--
<label for="esitoColloquio">Esito del Colloquio:</label>
<form:select id="esitoColloquio" name="esitoColloquio" path="esitoColloquio" tabindex="7">
<form:option value="positivo" >Positivo</form:option>
<form:option value="negativo" >Negativo</form:option>
<form:option value="altro"></form:option>
</form:select>-->
</p>
<p id="buttons">
<input id="reset" type="reset" tabindex="8">
<input id="submit" type="submit" tabindex="9"
value="Search">
</p>
</fieldset>
</form:form>
domain:
public class ColloquioSearch {
#Size(min=16, max=16)
private String codiceFiscale;
private String nome;
//#Size(min=1, max=50)
private String cognome;
private Date dataColloquio;
private Date dataIniz;
private Date dataFin;
private String esitoColloquio;
service:
public List<Colloquio> getAllColloquio()
throws SQLException {
List<Colloquio> result = new ArrayList<Colloquio>();
Statement s= con.createStatement();
ResultSet rs = s.executeQuery(
"SELECT doc.codice_fiscale, col.data_colloquio, col.esito_colloquio, col.note_colloquio FROM colloqui_pj col, docenti_pj doc where doc.id_docente=col.id_docente");
while(rs.next());{// il getTime per convertirla in util.date
result.add(new Colloquio(rs.getString(1), rs.getTime(2), rs.getString(3), rs.getString(4)));
System.out.println("rs has next");
}
return result;
}
search method called by submit button:
#RequestMapping(value="prova")
public String goSearch(Model model){
logger.info("here we are");
model.addAttribute("colloquioRicerca", new ColloquioSearch());
return "ColloquioSearchForm";
}
#RequestMapping(value="colloquio_ricerca")
public String cerca(#ModelAttribute ColloquioSearch colloquioRicerca, BindingResult br, Model model){
logger.info("modelattribute:"+colloquioRicerca.toString()+"/"+colloquioRicerca.getCodiceFiscale());
logger.info("entered");
if (br.hasErrors()) {
FieldError fieldError = br.getFieldError();
logger.info("Code:" + fieldError.getCode() + ", object:"
+ fieldError.getObjectName() + ", field:"
+ fieldError.getField()+"siamo qui");
model.addAttribute("colloquioRicerca", colloquioRicerca);
return "ColloquioSearchForm";
}//validare datafine minore data inizio
String codiceFiscale = colloquioRicerca.getCodiceFiscale();/*
Date dataColloquio = colloquioRicerca.getDataColloquio();
Date dataIniz = colloquioRicerca.getDataIniz();
Date dataFin = colloquioRicerca.getDataFin();*/
String nome = "%"+colloquioRicerca.getNome()+"%";
String cognome = "%"+colloquioRicerca.getCognome()+"%";
String esitoColloquio = colloquioRicerca.getEsitoColloquio();
Colloquio colloquioTrovato = null;
logger.info("siamo prima dell'if isEmpty");
if (!codiceFiscale.isEmpty()){
logger.info("dentro if is empty");
try{
logger.info("dentro il try:"+colloquioRicerca.getCodiceFiscale());
List<Colloquio> lista = colloquiService.getAllColloquio();
if(lista.isEmpty()){logger.info("lista nulla");}
for(Colloquio colloquio : lista){
logger.info("colloquio su db cf:"+colloquio.getCodiceFiscale());
if(colloquio.getCodiceFiscale().equals(codiceFiscale)){
colloquioTrovato=colloquio;
logger.info("siamo dentro l'if: colloquio trovato"+colloquioTrovato.getCodiceFiscale());
}
}
}
catch(SQLException e){logger.info(e.getMessage()+"siamo qui?");}
}
if (colloquioTrovato == null){
model.addAttribute("colloquioRicerca", colloquioRicerca);
return "ColloquioSearchForm";
}
return "Daje";
}
Below is the error I got in console when jsp processing stops:
/*dic 21, 2016 9:52:00 AM project.controller.ColloquioSearchController cerca
INFORMAZIONI: modelattribute:project.domain.search.ColloquioSearch#5d2ca87b/hereitwritesthecorrectparam
dic 21, 2016 9:52:00 AM project.controller.ColloquioSearchController cerca
INFORMAZIONI: entered
dic 21, 2016 9:52:00 AM project.controller.ColloquioSearchController cerca
INFORMAZIONI: Code:typeMismatch, object:colloquioSearch, field:dataColloquiosiamo qui
dic 21, 2016 9:52:00 AM org.springframework.web.servlet.tags.form.InputTag doStartTag
GRAVE: Neither BindingResult nor plain target object for bean name 'colloquioRicerca' available as request attribute*/
above is the error I got in console since i leave empty the date fields on form; I got the error for DataColloquio, then for DataFin, and then for DataIniz, subsequently. The only way to avoid this error is by filling the form imput fields related
I hope to be into the lines of this site asking for this, thank You all.
P.S.: I know the code is bad structured, and it is not a good way to code the way it is written, I just wanted to explain the issue, meanwhile I am changing the structure, I just do not understand why it is asking me for not leaving empty date fields, and not the others.
It might be better if you print these
Date dataColloquio = colloquioRicerca.getDataColloquio();
Date dataIniz = colloquioRicerca.getDataIniz();
Date dataFin = colloquioRicerca.getDataFin();
directly in your controller, as I kinda doubt that spring will parse the string format coming from your input into Date object without customization.
In short, if you want to bind the Date fields you could register custom data binding for the date via WebDataBinder by simply adds
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
in your controller, with this, even if your input is empty, spring will do the rest for you. You have to notice that the format would be yyyy-MM-dd or just change it if you want. As an alternative, you can also do
#DateTimeFormat(pattern = "yyyy-MM-dd") // Spring 4.0
private LocalDate dataColloquio;
#DateTimeFormat(pattern = "yyyy-MM-dd") // Spring 4.0
private LocalDate dataIniz;
#DateTimeFormat(pattern = "yyyy-MM-dd") // Spring 4.0
private LocalDate dataFin;
And about your error, this probably caused from your service as there's a rs.getTime(..) there, meanwhile the string date from your input cannot be parsed correctly into Date object thus your model object cannot be targetted hence thrown error. As previous my suggestion, try to put #InitBinder annotated method that I give above into your controller and let's hope it solves everything.

Format LocalDateTime with spring, jackson

In a spring rest application, i send a objet who contain a date
{ appointmentId: "", appointmentTypeId: "1", appointmentDate:
"2015-12-08T08:00:00-05:00" }
In my dto side
for my appointmentDate i have
#DateTimeFormat(iso=DateTimeFormat.ISO.DATE_TIME)
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime appointmentDate;
In my dependencies i have
jackson-datatype-jsr310-2.6.3
I get this error
rg.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: Text '2015-12-08T13:00:00.000Z' could not be
parsed, unparsed text found at index 23 (through reference chain:
server.dto.AppointmentDto["appointmentDate"]); nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Text
'2015-12-08T13:00:00.000Z' could not be parsed, unparsed text found at
index 23 (through reference chain:
server.dto.AppointmentDto["appointmentDate"])
tried only with DateTimeFormat, only with JsonDeserialize and both, but get the same error.
Edit
#RequestMapping(value = "/rest")
#RestController
public class LodgerController {
#RequestMapping(value = "/lodgers/{lodgerId}/appointments", method = RequestMethod.POST)
public Long createAppointmentsByLodgerId(#PathVariable("lodgerId") Long lodgerId, #RequestBody AppointmentDto appointmentDto) {
return appointmentService.save(appointmentDto);
}
}
public class AppointmentDto {
private Long appointmentId;
private Long appointmentTypeId;
private Long lodgerId;
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime appointmentDate;
public AppointmentDto() {
}
}
<form id="lodgerAppointmentForm" class="form-horizontal" role="form">
<input type="hidden" id="lodgerId" name="lodgerId">
<input type="hidden" id="lodgerAppointmentId" name="appointmentId">
<div class="form-group">
<label for="lodgerAppointmentDate" class="col-sm-2 control-label">Date</label>
<div class="col-sm-10">
<div class="input-group date" id="appointmentDatepicker" >
<input type="text" class="form-control" id="lodgerAppointmentDate" name="appointmentDate">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar">
</span>
</span>
</div>
</div>
</div>
</form>
var locale = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage);
moment().locale(locale);
$('#appointmentDatepicker').datetimepicker({
format: 'DD/MM/YYYY H:mm',
allowInputToggle: true
});
var lodgerId = $('#lodgerId').val();
var type = "post";
var url = "http://localhost:8080/rest/lodgers/" + lodgerId + "/appointments";
var data = transForm.serialize('#lodgerAppointmentForm');
data.appointmentDate = $('#appointmentDatepicker').data('DateTimePicker').date().format();
data.lodgerId = lodgerId;
data = JSON.stringify(data);
jQuery.ajax({
type: type,
url: url,
contentType: "application/json",
data: data,
success: function (data, status, jqXHR) {
},
error: function (jqXHR, status) {
}
});
transform.js come from https://github.com/A1rPun/transForm.js/blob/master/src/transForm.js
bootstrap datetimepicker come from https://github.com/Eonasdan/bootstrap-datetimepicker
Moment use 2015-12-09T08:00:00-05:00 (ISO 8601)
DateTimeFormatter.ISO_LOCAL_DATE_TIME who is: 2015-12-09T08:00:00 (ISO 8601)
both don't seem to use the same format
I think that your problem is described here:
https://github.com/FasterXML/jackson-datatype-jsr310/issues/14
I encounter the same error when was playing around with LocalDateTime and REST API. The problem is that you can serialize LocalDateTime to something like this:
2015-12-27T16:59:29.959
And you can create valid Date object in JavaScript from that string.
On the other hand if you try to POST/PUT JavaScript date to server then this:
var myDate = new Date();
JSON.stringify(myDate);
will create string like this (with extra Z - which stands for zulu time/UTC time zone):
2015-12-27T16:59:29.959Z
And that extra time zone info causes error in your case during deserialization because LocalDateTime don`t have time zone.
You can try to use ZonedDateTime on ther server or format by yourself date string on client side before sending (without Z suffiks).

play 2.0 form date validation fail

I'm using "Play 2.0"-Framework (v. 2.0.1) and running into some troubles with form validation of a date value.
Peace of my Model code:
#Entity
public class Appointment extends Model {
public Date start;
public Date end;
}
Peace of my template code:
<input type="text" id="start" name="start" placeholder="yyyy-mm-dd" />
<!-- ALSO tested with chrome beta v. 20 with html5 support-->
<input type="date" id="end" name="end" placeholder="yyyy-mm-dd" />
My Controller:
public class Appointment extends Controller {
static Form<Appointment> appointmentForm = form(Appointment.class);
//on calling form page
public static Result create() {
return ok(create.render("create", appointmentForm));
}
//called on saving form data
public static Result save() {
Form<Appointment> filledForm = appointmentForm.bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(
create.render("create", filledForm)
);
} else {
Appointment.create(filledForm.get());
return redirect(routes.Appointment.index());
}
}
}
If I select a date via jquery ui datepicker or type in myself in a format like "yyyy-mm-dd" or doesn't matter, but must be correct format I run into a validation error in my controller save()-method with the check "filledForm.hasErrors()" and the error message "wrong date format".
I thought it would be converted automatically from play, so that I don't have to add a convertion by my self. What can I do to solve this problem? Is it still an issue from play 2.0?
Thanky you.
Cheers,
Marco
I think you must define the format of the date. See Play Framework 2.0: Custom formatters for custom formatters. Perhaps a custom binder is necessary see https://groups.google.com/d/topic/play-framework/Xi2xAiCnINs/discussion
Just as some hints to give you a direction. Hopefully someone can give you a better answer.

Resources