GroupSequence and order of evaluation in JSR 303 - spring

I have a field of type String in a command bean which has to be validated in the following order.
Must contain a value (not empty).
Must have exactly 6 characters.
Must confirm this regexp - [0-9, a-f, A-F]+
When any of them is violated, the rest must not be performed. I have tried to achieve this using #GroupSequence as follows.
#GroupSequence({TempBean.ValidationGroupNotEmpty.class, TempBean.ValidationGroupColourHexLength.class, TempBean.ValidationGroup.class, TempBean.class})
public final class TempBean
{
#NotEmpty(groups={ValidationGroupNotEmpty.class}, message="Must enter a valid colour code.")
#Length(min=6, max=6, groups={ValidationGroupColourHexLength.class}, message="Requires exactly 6 characters.")
#Pattern(groups={ValidationGroup.class}, regexp="[0-9, a-f, A-F]+", message="Invalid colour code.")
private String stringValue;
public interface ValidationGroup{}
public interface ValidationGroupNotEmpty{}
public interface ValidationGroupColourHexLength{}
// Getters and setters.
}
When the text-field is intentionally left blank, only #NotEmpty is performed but when I enter a value that violates the #Length and the #Pattern constraints, I'm getting both the messages as specified meaning they both are evaluated. This shouldn't happen. Only one of them should be performed at a time in the defined order. How can this be done?
I'm using Spring 3.2.0 and Hibernate Validator 4.3.1.

You code looks fine and I even tested it against HV 4.3.1. It works as expected. You are evaluating the Default group right? Have you tried to debug?

Related

Exclude 0 from JSON response in Jackson Spring boot

I have a POJO like this.
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Test {
private int a;
private String b;
}
I want to exclude the property 'a' if it has 0 value. String b is excluded with
#JsonInclude(JsonInclude.Include.NON_NULL)
Only way I could thing of is convert the int data type to Integer Object and set the value to NULL in the setter explicitly if it is 0.
Any other suggestions or correct solution will be appreciated
Option 1:
Do what you said: Change int to Integer and use #JsonInclude(Include.NON_NULL). Because primitive types have default values and their values cannot be compared to null you have to wrap the int to Integer. See Primitive Data Types. imho this is the cleaner way.
Option 2:
Use the way described in this answer and use #JsonInclude(Include.NON_DEFAULT) instead (see Jackson-annotations API), so that default values (and so also null values for objects) will be ignored.
Note:
If you only want to exclude the specific field (in your case the int/Integer - a - field) when it has a null-/default value and the other fields (in your case the String - b - field) should be included when they have null-/default values, put the annotation on field level.

Spring Boot validation takes float value in integer field

I'm trying to implement a POST service with request validation.
My Controller method looks something like below
public void updateScore(#Valid ScoreRequest)
ScoreRequest looks like below
import javax.validation.constraints.*;
import lombok.Data;
#Data
public class ScoreRequest {
#Min(0)
#Max(100)
#Digits(fraction = 0, integer = 3)
private Integer score;
...
}
It all works fine till I pass integer values for score, however If I pass fraction part as well, request goes through and Spring somehow truncates the fraction and uses the integer part in the request.
I was expecting it to throw a validation error since datatype of score doesn't match.
It works with followling request, and uses 10 as the score value in the request object. I'm expecting it to throw an error, what am I doing wrong?
{"score": 10.234234}
Spring Boot version: 2.0.3.RELEASE
I was trying to debug Spring Boot's validation classes to find what was happening, but after looking at the comment by #M.Denium I searched for Jackson issues and found a related SO entry.
Java Jackson - prevent float to int conversion when deserializing
I'm using answer by #Jichao Zhang, however Just to confirm answer by #Eduardo Sanchez-Ros works as well. This is what works for me.
ObjectMapper.configure(DESERIALIZATION_FEATURE.ACCEPT_FLOAT_AS_INT, false);
Don't use this annotation: #Digits(fraction = 0, integer = 3 with Integer since it is useless to set fractions for Integer.
Why don't you do:
#Min(0)
#Max(100)
#Digits(fraction = 0, integer = 3)
private BigDecimal score;
If you closely look at the definition of the #digits annotation,
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
So this annotation can be applied to methods also.
If you read the docs, it says this annotation can be applied to the following types
BigDecimal
BigInteger
CharSequence
byte (or Byte)
short (or Short)
int (or Integer)
long (or Long)
the integer field of the annotation checks for the number of integral digits while the fraction field of the annotation checks for the number of fractional digits.
Since, you declared your field to be an Integer value. It casts the value to an integer and truncates the fractional part.
This does not fail validations as both the fields satisfy the validation.
This annotation is ideally to be only used with BigDecimal type.

spring #RequestBody validation fails

I have a very simple question related to the following line of code:
public void reject(#PathVariable int x, #Valid #NotEmpty #RequestBody String comments)
While debugging, the comments field has "" value and should fail.
Shall this work?
I know if I wrap comments in a object it will work but I am wondering why it is not working in this case.
i think, in you case, you have to use #NoBlank.
public void reject(#PathVariable int x, #Valid #NotBlank #RequestBody String comments)
See the difference here:
#NotNull: Checks whether the value is not null, disregarding the
content
#NotEmpty: Checks whether the value is not null nor empty. If it has
just empty spaces, it will allow it as not empty
#NotBlank: Checks whether the value is not null nor empty, trimming
the value first. It means that, it won’t allow just empty spaces
Source.

Spring MVC - Throws exception when the int value of ModelAttribute is null

I'm building an web application using Spring 3.0 MVC.
I have a method which has prototype below.
#RequestMapping(value = "/blahblah/blah.do", method=RequestMethod.GET)
public void searchData(#RequestParam(value="uniqOid", required=false) String uniqOid, #ModelAttribute("MasterVo") MasterVo searchVo,
ModelMap model, HttpServletResponse response, HttpServletRequest request)
The problem is that, the view (jsp) contains inputs that matches to searchVo(ModelAttribute).
When the int or long value of searchVo didn't come from the jsp, the server throws 404 page not found exception.
If the type of value is "String", it has no problem.
In my opinion, it is the problem of type casting.
How could I solve this problem, and which part of the server code that I have to check?
Thanks in advance.
I will go ahead and assume a few things about your problem.
It is not a type-cast problem. Spring has default converters that can easily convert from a String to some primitive type.
Now what you are facing is I think a null assigment to primitive type problem. Suppose the name of the property that's causing the problem is named primitiveProperty. Now, the request-paramters could include a parameter named primitiveProperty with an empty-String value, or some value that cannot be converted to a number. If the type of the primitiveProperty is String, it can assign the value of that parameter to it without any problem.
If the type of the primitiveProperty is int, long or some other primitive type that cannot have a null value, a problem occurs. When Spring converts the empty-string or a non-numeric string valued request-param named primitiveProperty, it cannot do so since that string can't be converted to a valid int or long value. So it is converted to null. Now, when Spring tries to assign that null value to a property that cannot have a null value (any primitve type), you get an Exception. If you are getting an empty-string as your request-param, you can replace the troublesome property in your domain object with its equivalent wrapper class (int with Integer, long with Long and so on). If you are getting a non-numeric value from your view, well, make sure that you don't get a non-numeric value.
You need to check the setter of the fields that are giving the typecast problem, in your case MasterVo .
The Spring will call the setter of the property to bind the value, where i presume you will see the error coming.
Just add a debug point to this setter and you will see the problem.

Where does Grail's errors property come from?

Grails has a bug with regards to databinding in that it throws a cast exception when you're dealing with bad numerical input. JIRA: http://jira.grails.org/browse/GRAILS-6766
To fix this I've written the following code to manually handle the numerical input on the POGO class Foo located in src/groovy
void setPrice(String priceStr)
{
this.priceString = priceStr
// Remove $ and ,
priceStr = priceStr.trim().replaceAll(java.util.regex.Matcher.quoteReplacement('$'),'').replaceAll(',','')
if (!priceStr.isDouble()) {
errors.reject(
'trade.price.invalidformat',
[priceString] as Object[],
'Price:[{0}] is an invalid price.')
errors.rejectValue(
'price',
'trade.price.invalidformat')
} else {
this.price = priceStr.toDouble();
}
}
The following throws a null reference exception on the errors.reject() line.
foo.price = "asdf" // throws null reference on errors.reject()
foo.validate()
However, I can say:
foo.validate()
foo.price = "asdf" // no Null exception
foo.hasErrors() // false
foo.validate()
foo.hasErrors() // true
Where does errors come from when validate() is called?
Is there a way to add the errors property without calling validate() first?
I can't exactly tell you why, but you need to call getErrors() explicitly instead of accessing it as errors like a property. For some reason, Groovy isn't calling the method for it. So change the reject lines in setPrice() to
getErrors().reject(
'trade.price.invalidformat',
[priceString] as Object[],
'Price:[{0}] is an invalid price.')
getErrors().rejectValue(
'price',
'trade.price.invalidformat')
That is the easiest way to make sure the Errors object exists in your method. You can check out the code that adds the validation related methods to your domain class.
The AST transformation handling #Validateable augments the class with, among other things
a field named errors
public methods getErrors, setErrors, clearErrors and hasErrors
The getErrors method lazily sets the errors field if it hasn't yet been set. So it looks like what's happening is that accesses to errors within the same class are treated as field accesses rather than Java Bean property accesses, and bypassing the lazy initialization.
So the fix appears to be to use getErrors() instead of just errors.
The errors are add to your validateable classes (domain classes and classes that have the annotation #Validateable) dinamically.
Allowing the developer to set a String instead of a number doesn't seem a good way to go. Also, your validation will work only for that particular class.
I think that a better approach is to register a custom property editor for numbers. Here's a example with dates, that enable the transform of String (comming from the form) to Date with a format like dd/MM/yyyy. The idea is the same, as you will enforce that your number is parseable (eg. Integer.parseInt() will throw exception).
In your domain class, use the numeric type instead of String, so by code developers will not be allowed to store not number values.

Resources