I'm using Spring MVC with JSR303 to do my input validation.
A form I've created has a couple of date fields that are bound to Date objects within the object backing the form. I'm using JSR303 to do the validation for the Date using #Future. I'm also using #DateTimeFormat(pattern="dd/MM/yyyy"), (I know it's not validation).
How do I validate the date format of the String on the form? If I leave the other required fields blank (#NotEmpty) and enter a non-valid date in the form 'dd/MM/yy' it gets converted to 'dd/MM/yyyy' on re-presentation (e.g. 12/03/12 is re-presented as 12/03/0012). Which means I will get duff data in my system. If I enter "aaa" for I get a conversion Exception. Correctly formatted Strings get converted to Date objects.
Additionally should the 'required field' annotation for Date fields be #NotNull or #NotEmpty?
Many thanks in advance for any advice provided.
Thanks Ralph. I did some further digging around and came up with this (Which goes in my form controller):
#InitBinder
public void initBinder(WebDataBinder binder) {
String format = "dd/MM/yyyy";
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);
CustomDateEditor customDateEditor = new CustomDateEditor(dateFormat,true,format.length());
binder.registerCustomEditor(Date.class, customDateEditor);
}
With the properties file having the following key:
typeMismatch.java.util.Date : Some nice calm reassuring message to assist all negligent users
Maybe there are some other ways to do this but this will do for now.
You can not do this with JSR303, because the validation runs on the already poplulated (form baching) object.
So you need to implement your own custom converter, that is a bit more strickt than the one shipped with spring.
#See Spring Reference: Chapter 6.5 Spring 3 Type Conversion
Related
I'm unsure how YAML "timestamps" are supposed to be represented. From the YAML 1.2 specification, it would seem that YAML just assumes that if you have a value in string format that looks like an ISO 8601 date, then it is parsed as a date unless you say differently. Here are a couple of examples from the spec:
date: 2002-12-14
not-date: !!str 2002-04-28
The Timestamp Language-Independent Type for YAML™ Version 1.1 working draft (from 2005, currently 15 years ago!) seems to indicate that a special tag in the form tag:yaml.org,2002:timestamp should be used. It also indicates a shorthand of !!timestamp.
In Java using SnakeYAML Engine v2.0 (org.snakeyaml:snakeyaml-engine:2.0) I tried parsing the 2002-12-14 form, and got a string as the parsed value, not any sort of date object. I see that the SnakeYAML Engine repository has an example using the !!timestamp approach (e.g. !!timestamp 2020-03-24T12:34:00.333), but this is a recent change and I'm sure if this support has been released yet.
I tried both the form fooBar: !!timestamp 2020-03-24 and also fooBar: !!timestamp 2020-03-24T12:34:00.333, but SnakeYAML Engine reported:
could not determine a constructor for the tag tag:yaml.org,2002:timestamp
So what is the official way to represent a date (specifically a local date with YYYY-MM-DD) in YAML, and is the correct approach reflected in the latest YAML specification? Does SnakeYAML Engine support the official YAML date approach?
From the YAML 1.2 specification, it would seem that YAML just assumes that if you have a value in string format that looks like an ISO 8601 date, then it is parsed as a date unless you say differently.
No. The YAML spec gives three schemas (Failsafe, JSON and Core) that should be supported; none of them includes a timestamp type. However, a scalar looking like a timestamp can be parsed as such if a schema is used that supports it. The spec only tells you that if you want to ensure that a scalar is not loaded as timestamp, prefix it with !!str.
So what is the official way to represent a date (specifically a local date with YYYY-MM-DD) in YAML.
The !!timestamp definition you linked is closest to what an official way would be. However, the tag repository containing it is not part of the spec and implementations are not required to support it. Furthermore, it is defined for outdated YAML 1.1.
This means that SnakeYAML isn't required to support timestamps at all. You can see in the example you give that timestamp support is not included; the example implements loading timestamps itself. You can modify that code to use with the normal public interface:
class TimestampConstructor extends Constructor {
public static final Pattern TIMESTAMP = Pattern
.compile("^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[ \t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](?:\\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$");
public static final Tag TAG = new Tag(Tag.PREFIX + "timestamp");
public TimestampConstructor() {
this.yamlConstructors.put(TAG, new ConstructTimestamp());
}
private class ConstructTimestamp extends AbstractConstruct {
public Object construct(Node node) {
String val = (String) constructScalar(node);
return LocalDateTime.parse(val);
}
}
}
Then, use it like this:
Yaml yaml = new Yaml(new TimestampConstructor());
yaml.addImplicitResolver(TimestampConstructor.TAG,
TimestampConstructor.PATTERN, "0123456789");
I have an Angular WebApp who retrives data from a backend Java server via http get.
Example:
public getStateForUser(): Observable<UserRegistrationState> {
return this.http.get<any>(Environment.getServerAddress() + '/' +
UserRegistrationStateService.SERVICE_URL + '/getStateForUser', {
params: {
sessionIdentityStr: this.authGuard.getSession().sessionIdentityStr
}
});
}
export class UserRegistrationState {
registerDate: Date;
emailVerified: boolean;
unlocked: boolean;
}
This works fine for all fields. Except the Dates. The Dates are not parsed correctly, so I want to add a custom parser where I can tell him how he gets the date value from the json date string.
Of course now I can add a custom Parser for the whole UserRegistrationState class, but I have Dates on multiple classes and I do not want to create a Parser for every class, because the other properties(except the Dates) are parsed fine.
So my question: Is there a way to add a custom parser for all Date fields?
P.S. This is an example JSON date string, automatically parsed from a Java LocalDateTime:
"registerDate":{"dayOfMonth":17,"dayOfWeek":"WEDNESDAY","dayOfYear":290,"month":"OCTOBER","monthValue":10,"year":2018,"hour":21,"minute":21,"nano":370000000,"second":16,"chronology":{"id":"ISO","calendarType":"iso8601"}}
This unfriendly date format is probably caused by backend. I never had any problems with dates from any API in Angular.
You can create getters/setters to encapsulate custom parsing logic for some field in your class.
In my opinion would be better to fix backend side and move from classes to interfaces in Angular side. Interfaces are better for plain DTO models in TypeScript.
I have an XML file in format described below. I'm currently using JAXB to unmarshall the XML elements (event) to Java objects (Event). All properties work fine, except one...
As you can see in the XML, the date element stores only the date and the time stores only the time of the event. I would like to combine these two XML elements into one LocalDataTime field named time, with appropriate getters and setters.
XML:
...
<event>
...
<date>2014-02-19</date>
<time>2000-01-01T14:17:00Z</time>
...
</event>
...
Desired Java Object:
public class Event {
...
// Returns a combination of the date and time stored in the XML
public LocalDateTime getDateTime() {
return dateTime;
}
...
}
My first thought was to use a XMLAdapter but it seems that this only allows me to map 1 XML element to 1 Java object.
After that I tried to implement this by overriding the setters setTime and setDate. These setters would each just change the time or date of the stored time.. But I wasn't able to get it working and it also seemed a quite ugly way to do this.
I already read the following JAXB convert multiple attributes to one type while unmarshalling. But for this project, I would rather not use an external library like MOXy.
Does anyone know how to do this using solely JAXB in a clean way?
You could define in your Event object lifecycle methods:
void afterUnmarshal(Unmarshaller unm, Object parent) {
localDateTime = ....
};
boolean beforeMarshal(Marshaller mar) {
date = localDateTime.toDate();
....
};
to construct the LocalDateTime property after unmarshalling (using the date and time values), and prepare date/time fields before marshalling using the current LocaLDateTime value.
You would still need the time/date fields to match the xml and the localDateTime field must be XmlTransient. So it is not so different from the set/getter approach but probably more "clean".
In my Spring Web MVC application, I have a bunch of methods in #Controllers that accept a Date as an input parameter with #RequestParam. Without defining any custom data binders or property editors (I admit I'm still not clear on the difference between those two), what date formats are supported by default? For example, I've noticed that something like '11/12/2012 16:50 PM' works fine, but a plain milis value like '1352815200000' is rejected.
Edit: the specific exception I get is: "Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date]: no matching editors or conversion strategy found"
I believe that if no converter is specified, Spring convertion system will end up calling the deprecated Date constructor, that takes String as an argument.
/**
* Allocates a <code>Date</code> object and initializes it so that
* it represents the date and time indicated by the string
* <code>s</code>, which is interpreted as if by the
* {#link Date#parse} method.
*
* #param s a string representation of the date.
* #see java.text.DateFormat
* #see java.util.Date#parse(java.lang.String)
* #deprecated As of JDK version 1.1,
* replaced by <code>DateFormat.parse(String s)</code>.
*/
#Deprecated
public Date(String s) {
this(parse(s));
}
usually the applications have different requirements - some work with the local time(also browser specific things and user time zone settings may come into play), some don't need to store the date - so it's better to implement a policy for date conversion both on client and server side.
P.S. please note that Date.parse() is also deprecated.
The allowed date formats probably depend on your current Locale.
You can add a Handler that converts the date for the formats you like.
I'm not really sure which one was it. #InitBinder shows an example of converting a date from whatever format..
hope this works for you.
Create a class Register an editor for your date formate.
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry
import org.springframework.beans.propertyeditors.CustomDateEditor
import java.text.SimpleDateFormat
public class CustomDateEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
String dateFormat = 'yyyy/MM/dd'
registry.registerCustomEditor(Date, new CustomDateEditor(new SimpleDateFormat(dateFormat), true))
}
}
I use Struts 1.
And have next bean
import java.util.Date;
public class Event {
private String name;
private Date date;
// getters & setters
}
And form
public class Holiday {
private Event event;
// getters & setters
}
I have jsp for adding new holiday
<html:text name="holidayForm" property="event.date" altKey="date.pattern" maxlength="10" size="10" /></label>
And I cant get date or put default date as Date date = new Date(); in particular format on jsp.
How can I do this?
Rather than making your field a Date, make it a String that represents the date in the correct format. You can use the SimpleDateFormatter class to convert between the two. You could also take a look at the jQuery UI Datepicker module (Javascript, so it will run client-side) to allow your users to select a date (in the correct format), rather than typing it in themselves.
Struts 1 form bean properties are almost always Strings because the built-in type conversion is quite limited. If you map a text input directly to a Date field in your form bean class, the value is expected to be in the format YYYY-MM-DD and you'll get an Error 500: beanUtils.populate error if it doesn't match.