spring validator #Pattern doesn't work on Integer - spring

I am trying a simple pattern validation:
#Min(value = 0, message = "invalid.amount")
#Pattern(regexp = "[0-9]+", message = "invalid.amount")
private double amount;
But it gives the error like:
org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229)
But this works perfectly fine:
#Pattern(regexp = "[\\w|-]{1,5}")
private String data;
It seems regex doesn't work on number types.

You are assuming that the #Pattern annotation will return true if a substring regex match passes. If it isn't working then your assumption may not be true.
#Pattern(regex=,flag=) CharSequence
Checks if the annotated string matches the regular expression regex
considering the given flag match.
You can try change double to String and works with #Pattern normally.
#Pattern(regexp = "[0-9]+", message = "invalid.amount")
private String amount;
Also other way could be:
#DecimalMax("10.0") #DecimalMin("0.0")
private double amount;

Related

Can we have two #PathVariable. One as the actual Path Variable and other for swagger to document it as Deprecated?

I am trying to change the REST call's #PathVariable. The existing Path Variable is formed by combination of three parameters. The change is to handle Path Variable formed by combination of two parameters. I need this change to be documented in swagger with the earlier shown as Deprecated.
I have tried to use both Path Variable with one as #Deprecated like below
#Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", required=false )
#Deprecated #PathVariable String x_y_z,
#Parameter(description = "x_y - x is first ID, y is second ID", required=true )
#PathVariable String x_y)
The have changed request mapping value from /aaa/bbb/{x_y_z} to below
#RequestMapping(value = "/aaa/bbb/{x_y}", method = RequestMethod.GET, produces = "application/json"
With above changes the request fails with 500 error, may be since it is expecting two Path Variables. But the swagger documentation is as expected.
I tried to remove #PathVariable for x_y_z. The request is processed as expected and the swagger now shows x_y_z as deprecated but shows the parameter as (query) instead of (path)
Any suggestions please
Assuming an #RestController and that Swagger understands #Deprecated for a method:
#Deprecated
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}_{z:\\d+}")
public ResponseEntity<MessageResponse> getStuff(#PathVariable String x,
#PathVariable String y,
#PathVariable(name = "z", required = false) String z) {
return getNewStuff(x, y); //send to the other method and ignore z
}
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}")
public ResponseEntity<MessageResponse> getNewStuff(#PathVariable String x,
#PathVariable String y) {
// do stuff for x and y by default
return ResponseEntity.ok(new MessageResponse("this method is supported for " + x + " and " + y));
}
The RegEx should look for digits as the path variables, interspersed with underscores.
NB: leaving this part of the answer if Swagger works with it instead with the understanding that it could be deprecated:
#PathVariable #Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", deprecated = true) String z
Deprecating the original method and introducing a new method with the correct parameters but different RequestMapping could also be a valid workaround.
The other part to note is that it is more common to use slashes as the delimiter rather than underscores in Spring (e.g., /aaa/bbb/x/y). You also may wish to include a validator that fits your requirements.

How to avoid #RequestParam concatenating when a "+" operator is recieved as parameter?

I'm new to Spring Framework and I'm trying to build this simple calculator app.
I've got this #RestController with this method:
#RequestMapping(value = "/calculate", method = RequestMethod.GET)
public String calculate(#RequestParam(value = "a") String a, #RequestParam(value = "b") String b, #RequestParam(value = "operator") String operator) {
//System.out.println(operator);
if(operator.equals("+")){
return String.valueOf((Integer.valueOf(a) + Integer.valueOf(b)));
}
if(operator.equals("-")){
return String.valueOf((Integer.valueOf(a) - Integer.valueOf(b)));
}
if(operator.equals("*")){
return String.valueOf((Integer.valueOf(a) * Integer.valueOf(b)));
}
if(operator.equals("/")){
return String.valueOf((Integer.valueOf(a) / Integer.valueOf(b)));
}
Ok, the problem here is that when I send a "+" parameter to sum the two variables the program is concatenating both int's instead of performing the sum.
The rest of the operations are working fine, except for the sum. I've tried sending a "/+" without luck.
Any idea how this can be solved and most importantly, why is this happening ?
Thanks a lot :D
You should be aware of that + is a special character. To use + as a value you need to encode it as %2B in your GET request.
For example:
http://localhost:8080/calculate?a=3&b=5&operator=%2B

Java 8 DateTimeFormatter with optional part

I have a String representing a date (with or without time) like 13/12/2017 or 13/12/2017 15:39:51
So i'm trying to use java 8 DateTimeFormatter with optional part.
That code works
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017 15:39:51",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
13/12/2017
15:39:51
But I don't understand why that one does not
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]"));
giving me
Exception in thread "main" java.time.format.DateTimeParseException: Text '13/12/2017' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2017-12-13 of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
...
And even with
LocalDateTime localDateTime = LocalDateTime.parse("13/12/2017",DateTimeFormatter.ofPattern("dd/MM/yyyy"));
it does not work with the same exception.
Use parseBest
When you use an optional component, you should parse using parseBest. Your application may be working using only parse, but then it's only by luck (because you're only parsing full inputs, not partial ones). With parseBest, you can properly handle various TemporalAccessor, which is the whole reason to use optional.
The decision of which TemporalAccessor is returned is rather simple: parseBest will try to match each TemporalQuery in order of argument. When any matches, the method returns that one. So make sure to go from most precise to less precise. Also, if none were matched, an exception will be thrown.
LocalDateTime dateTime;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy[ HH:mm:ss]");
TemporalAccessor temporalAccessor = formatter.parseBest("13/12/2017", LocalDateTime::from, LocalDate::from);
if (temporalAccessor instanceof LocalDateTime) {
dateTime = (LocalDateTime)temporalAccessor;
} else {
dateTime = ((LocalDate)temporalAccessor).atStartOfDay();
}

Java8 LocalDateTime to XMLGregorianCalender Remove "+05:30" Portion

Did like below,
LocalDateTime currentUTCTime = LocalDateTime.now(ZoneId.of("UTC"));
String reqPattern = currentUTCTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS"));
System.out.println("Required pattern: " + reqPattern);
GregorianCalendar calendar = GregorianCalendar.from(currentUTCTime.atZone(ZoneId.systemDefault()));
XMLGregorianCalendar xcal = DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);
System.out.println("But Showing As :" + xcal);
I want the output as 2015-06-18 11:59:15:135, but when i set the xcal to
a XML tag which takes XMLGregorianCalender, it shows like 2015-06-18T11:59:15.135+05:30.
How can i remove the +05:30 portion?
Use this code:
LocalDateTime currentUTCTime = LocalDateTime.now(); // using system timezone
String iso = currentUTCTime.toString();
if (currentUTCTime.getSecond() == 0 && currentUTCTime.getNano() == 0) {
iso += ":00"; // necessary hack because the second part is not optional in XML
}
XMLGregorianCalendar xml =
DatatypeFactory.newInstance().newXMLGregorianCalendar(iso‌​);
Explanation:
The code makes use of the given factory method expecting a lexicographical representation of a local timestamp in ISO-8601-format. And since a LocalDateTime does not refer to any timezone, its output via toString() cannot contain a timezone offset. Result: XMLGregorianCalendar considers the timezone offset as "not set".
Correction:
The original code did not especially bother about the ISO-variant of formatted output of currentUTCTime.toString(). However, the java.time-API produces an output without seconds or nanoseconds if those parts are equal to zero. This is perfectly legal in ISO, but the W3C-consortium has made the second part non-optional. And the class XMLGregorianCalendar closely follows this deviating spec. Therefore the shown hack above using simple string concatenation in this special edge case. Thanks a lot to #Dave's comment. By the way, using currentUTCTime.format(DateTimeFormatter.ISO_DATE_TIME) as suggested in this comment is also possible (instead of the shown hack).

Parsing double superCSV with comma as decimal separator?

I Want to parse a double with comma as decimal separator (',' instead of '.') using SuperCSV CellProcessor
I want to parse the first element (0,35) to Double
0,35;40000,45
I have tried something like that :
/** FRENCH_SYMBOLS */
private static final DecimalFormatSymbols FRENCH_SYMBOLS = new DecimalFormatSymbols(Locale.FRANCE);
DecimalFormat df = new DecimalFormat();
df.setDecimalFormatSymbols(FRENCH_SYMBOLS);
final CellProcessor[] processors = new CellProcessor[] {
new NotNull(new ParseDouble(new FmtNumber(df))),
new NotNull(new ParseBigDecimal(FRENCH_SYMBOLS)) };
ParseBigDecimal works just fine but the parseDouble doesn't seems to work, it gives me an exception : org.supercsv.exception.SuperCsvCellProcessorException: '0,35' could not be parsed as a Double
You're totally correct - ParseDouble doesn't support a French-style decimal separator (comma), but ParseBigDecimal does. If you think this is a useful feature, why not submit a feature request.
The simplest workaround is to simply chain a StrReplace before the ParseDouble to convert the comma to full stop.
new StrReplace(",", ".", new ParseDouble())
Alternatively, you could write a custom cell processor that either:
parses a Double (with a configurable decimal separator)
converts a BigDecimal to a Double (calling doubleValue()) - this can then be chained after your new ParseBigDecimal(FRENCH_SYMBOLS)
Oh, and in future you might want to mention that your file is semi-colon separated and you've set up Super CSV with CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE :)

Resources