I am trying to validate the postal code in my but the approach i am thinking of is not working out and I can't understand why.
I created a Validator, that hast to throw a ValidationException if it's not valid.
#Service
public class ZipCodeValidator{
public void validate(String zipCode){
validateNotEmpty(zipCode);
validateHasNoSpaces(zipCode);
}
private void validateNotEmpty(String zipCode){
if (zipCode.length() != 0){
throw new ValidationException("Postal Code can't be empty");
}
}
private void validateHasNoSpaces(String zipCode) {
if (zipCode.contains(" ")){
throw new ValidationException("Postal Code can't contain spaces");
}
}
}
In my service, where i receive the postal code I want to throw my custom exception (to which i pass the error message) like this:
try{
validator.validate(zipCode);
}catch (ValidationException ex){
throw new ZipCodeValidationException(ex.getMessage());
}
However it doesn't seem to work, that exception is not caught so the program runs further.
What am I doing wrong?
Is the whole approach wrong? What would you recommend?
Here's my custom Exception
public class ZipCodeValidationException extends Exception{
public ZipCodeValidationException(String message) {
super(message);
}
}
I recommend the following:
to process all exceptions in universal place as ExceptionHandler class, for more details see: https://www.baeldung.com/exception-handling-for-rest-with-spring
you can extend ValidationException from RuntimeException, that approach will allow unloading the code from try-catch constructions
#Service annotation is not quite right for converters or validators, as rule #Service class contains business logic, for helpers classes will be better use #Component, in total no differences between these two annotations only understanding which layer of application that component has
Please share the code for more suggestions and help.
Hi Please find my answer in 2 steps, first the correct and then the second the suggested way to implement.
Correction:
Please use ObjectUtils.isEmpty(arg) for checking if string is 0 length or null. Here is the modified version of your code
public interface ZipcodeService {
void validate(#Zipcode String zipCode) throws ZipCodeValidationException;
}
#Service
public static class ZipcodeServiceImpl implements ZipcodeService {
private final ZipCodeRegexMatcher zipCodeRegexMatcher;
public ZipcodeServiceImpl() {
zipCodeRegexMatcher = new ZipCodeRegexMatcher();
}
#Override
public void validate(String zipCode) throws ZipCodeValidationException {
// uncomment for Regex Validation
// boolean valid = zipCodeRegexMatcher.isValid(zipCode);
// uncomment for Simple text validation
final boolean valid = !ObjectUtils.isEmpty(zipCode);
if (!valid) {
throw new ZipCodeValidationException("Invalid zipcode");
}
}
}
This is how the caller looks like from Controller
#GetMapping(path = "dummy")
public String getDummy(#RequestParam("zipcode") String zipCode) {
try {
zipcodeService.validate(zipCode);
return zipCode;
} catch (ZipCodeValidationException e) {
return e.getMessage();
}
}
Suggested way:
add following entry to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Create Annotation and Validator as given below
#Constraint(validatedBy = {ZipcodeValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Zipcode {
String message() default "Invalid Zipcode value";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class ZipcodeValidator implements ConstraintValidator<Zipcode, String> {
private final ZipCodeRegexMatcher zipCodeRegexMatcher;
public ZipcodeValidator() {
zipCodeRegexMatcher = new ZipCodeRegexMatcher();
}
#Override
public boolean isValid(String zipCode, ConstraintValidatorContext constraintValidatorContext) {
return zipCodeRegexMatcher.isValid(zipCode);
}
}
Once this setup is done, head over to Controller class and annotated class with
#Validated and field you want to have validation on with the Custom Annotation i.e Zipcode we have just created. We are creating a Custom Validator in this case ZipcodeValidator by extending ConstraintValidator.
This is how the caller looks like:
#GetMapping
public String get(#Zipcode #RequestParam("zipcode") String zipCode) {
return zipCode;
}
On Failed validation, it throws javax.validation.ConstraintViolationException: get.zipCode: Invalid Zipcode value which you can customize according to your need by using ControllerAdvice.
You can also use #Zipcode annotation at the service level and it works the same way. Regarding ZipCodeRegexMatcher instead of creating it inside the constructor you can create a bean and inject that dependency. It is a simple class that has regex for zipcode and performs validation.
public static class ZipCodeRegexMatcher {
public static final String ZIP_REGEX = "^[0-9]{5}(?:-[0-9]{4})?$";
private final Pattern pattern;
public ZipCodeRegexMatcher() {
pattern = Pattern.compile(ZIP_REGEX);
}
public boolean isValid(String zipCode) {
return pattern.matcher(zipCode).matches();
}
}
The entire code is located here
Related
I'm working on a base repo in which I want to add a custom validation example in the rest layer, but it is not working, is not printing the flags at the moment of the validation, it just go right through the controller layer instead of the validator class.
What am I missing?
This is the rest layer:
#RequestMapping("equipment/")
public interface DeviceREST extends RestConstants {
#GetMapping("test")
#Produces(MediaType.APPLICATION_JSON)
BaseResult<UserDTO> test(
/* Example of a validation layer before moving forward device impl */
#QueryParam("genre") #Valid #AllowedGenderExampleValidations final String genre
) throws Exception;
}
AllowedGenderExampleValidations class
#Documented
#Retention(RUNTIME)
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Constraint(validatedBy = ExampleValidationsValidator.class)
public #interface AllowedGenderExampleValidations {
String message() default "Invalid value for genre query param";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class ExampleValidationsValidator implements ConstraintValidator<AllowedGenderExampleValidations, String> {
private List<String> allowedGenres = new ArrayList<>();
#Override
public void initialize(AllowedGenderExampleValidations constraint) {
System.out.println("##########################################################################");
List<GenderExampleEnum> allowedValues = new ArrayList<>(EnumSet.allOf(GenderExampleEnum.class));
for(GenderExampleEnum g : allowedValues) {
this.allowedGenres.add(g.name().toLowerCase());
}
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("##########################################################################");
return value != null && this.allowedGenres.contains(value) && this.allowedGenres.contains(value.toLowerCase());
}
}
GenderExampleEnum class
public enum GenderExampleEnum {
M("Man"),
W("Woman"),
O("Other");
private final String genre;
public String getGenre() { return genre; };
GenderExampleEnum(String genre) { this.genre = genre; }
public static GenderExampleEnum fromValue(String code) throws IllegalArgumentException {
for(var g : GenderExampleEnum.values()) {
if(code.toLowerCase().equalsIgnoreCase(g.name())) {
return g;
}
}
return GenderExampleEnum.O;
}
}
Controller class
#Controller
public class DeviceImpl implements DeviceREST {
private static final Logger logger = LoggerFactory.getLogger(DeviceImpl.class);
#Autowired private DeviceService deviceService;
#Autowired private DataTransferUtil dataTransferUtil;
#Override
public BaseResult<UserDTO> test(String genre) throws Exception {
var serviceResponse = deviceService.testFirstMethod();
var mappingResponse = dataTransferUtil.mapFirstTestMethod(serviceResponse);
return new BaseResult<UserDTO>(mappingResponse);
}
}
Test response, missing validations for query param
URL: localhost:8080/equipment/test?genre=o
I can't derive for sure from the context which Spring Boot version you are using, but for Spring boot 2.3.0 or later, a common mistake is relying on the spring-boot-starter-validation that is not included by default anymore.
You should make sure to add the following dependency if you want to use validation in a Spring Boot 2.3.0 or up project:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
See spring docs here.
Could you please let me know if this solved your issue by replying or accepting this answer?
I should have added this annotation in the Rest Layer -> "#Validated" to make it work, without that dependency, validators won't be triggered
I am looking to field validate a web service request by only allowing one of two different fields in the request. I know from past experience from using xsd that you can have something like this to only allow either FieldOne or FieldTwo:
<xs:complexType name="SomeType">
<xs:choice>
<xs:element name="FieldOne" type="target:FieldOneType"/>
<xs:element name="FieldTwo" type="target:FieldTwoType"/>
</xs:choice>
</xs:complexType>
I would like to do the same using Java annotations. I am currently using annotations for limiting field length (#Digits) and null checks (#NotNull).
Is there something I can use for a 'choice'?
Thanks for any help.
UPDATE: Basically I am looking for some way of only allowing one of two different fields to be entered in a web service request without having to do this validation in my code manually. I am currently using bean validation annotations to limit field lengths and to determine whether a field is mandatory or optional e.g.:
#NotNull(message="Field cannot be empty")
#Size(max = 6, message = "Field length is too long")
private String fieldOne;
I would like to be able to say that the user can only enter either fieldOne or fieldTwo, but not both. Is this possible through annotations or am I stuck to writing this validation in my code?
Edited:
To validate either one field has value but not other, I think you can use a custom validator at class level. Following is the idea:
1.
Create interface for your annotation:
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ChoiceValidator.class)
#Documented
public #interface Choice {
String[] fields();
String message() default "{Choice.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.
Create a implementation of ConstraintValidator to check the value to be validated is in fields inside Choice annotation:
public class ChoiceValidator
implements ConstraintValidator<Choice, Object> {
private List<String> fields;
#Override
public void initialize(final Choice choice) {
fields = Arrays.asList(choice.fields());
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext ctx) {
int nonNullFieldCount = 0;
for (String field : fields) {
try {
final String fieldValue = BeanUtils.getProperty(value, field);
if (fieldValue != null) {
nonNullFieldCount++;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return nonNullFieldCount == 1;
}
}
After that, you can use it something like:
#Choice(fields= {"fieldOne", "fieldTwo"})
public class Foo {
String fieldOne;
String fieldTwo;
}
Original:
I am not sure I really get you mean, but looks like you want a validation on the Class types of a Object field. If there is the case, you may try to create your custom annotation and ConstraintValidator to do so. Following is the idea:
1.
Create interface for your annotation:
public #interface Choice {
Class<?>[] types();
}
2.
Create a implementation of ConstraintValidator to check the value to be validated is in types inside Choice annotation:
public class ChoiceValidator implements ConstraintValidator<Choice, Object> {
private List<Class<?>> clazzes;
#Override
public void initialize(Choice choice) {
clazzes = Arrays.asList(choice.types());
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
for (Class<?> clazz : clazzes) {
if (value.getClass().equals(clazz)) {
return true;
}
}
return false;
}
}
After that, you can use it something like:
#Choice(types = {FieldOneType.class, FieldTwoType.class})
public class Foo {
Object someType;
}
Hope this can help.
Im using a custom validation on a textbox that checks if entered sentence contains only white spaces
The code is as below
#Retention(value = RetentionPolicy.RUNTIME)
#javax.validation.Constraint(validatedBy = SmsMessageContent.SmsMessageContentValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE })
#Documented
public #interface SmsMessageContent {
public class SmsMessageContentValidator implements
ConstraintValidator<SmsMessageContent, String> {
#Override
public void initialize(final SmsMessageContent constraintAnnotation) {
}
#Override
public boolean isValid(final String value,
final ConstraintValidatorContext context) {
if (value == null)
return true;
try {
if (value.matches("^\\s*$")) {
context.buildConstraintViolationWithTemplate("{message.sms.content.not.empty}")
.addConstraintViolation();
context.disableDefaultConstraintViolation();
return false;
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
}
The above annotation im using in another class as
public class SmsMessageForm {
#SmsMessageContent
private String smsChannelContent = "";
Is there any annotation in JSR303 or JSR330 that can be used directly on
private String smsChannelContent = "";
instead of writing another annotation like what I have used to check for a whitespaces only?
I believe #Pattern does just that
#Pattern("^\\s*$")
private String smsChannelContent = "";
The javadoc states
The annotated String must match the following regular expression.
So use the pattern you need. I wasn't sure if you wanted to check against whitespace or for whitespace.
I have the following class:
public class Message {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
When converting the instance to JSON using Jackson by default I get:
{"text":"Text"}
I would like to get:
{"message":{"text":"Text"}}
Is there any JAXB / Jackson annotation I can use to achieve my goal?
As a workaround, I can wrap my class with another class:
public class MessageWrapper {
private Message message;
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
}
or a more generic solution:
public class JsonObjectWrapper<T> {
/**
* Using a real map to allow wrapping multiple objects
*/
private Map<String, T> wrappedObjects = new HashMap<String, T>();
public JsonObjectWrapper() {
}
public JsonObjectWrapper(String name, T wrappedObject) {
this.wrappedObjects.put(name, wrappedObject);
}
#JsonAnyGetter
public Map<String, T> any() {
return wrappedObjects;
}
#JsonAnySetter
public void set(String name, T value) {
wrappedObjects.put(name, value);
}
}
Which can be used like so:
Message message = new Message();
message.setText("Text");
JsonObjectWrapper<Message> wrapper = new JsonObjectWrapper<Message>("message", message);
Is there any JAXB / Jackson annotation I can use to achieve my goal?
Thanks.
With Jackson 2.x use can use the following to enable wrapper without adding addition properties in the ObjectMapper
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
#JsonTypeName(value = "student")
public class Student {
private String name;
private String id;
}
On workaround: you don't absolutely need those getters/setters, so could just have:
public class MessageWrapper {
public Message message;
}
or perhaps add convenience constructor:
public class MessageWrapper {
public Message message;
#JsonCreator
public MessageWrapper(#JsonProperty("message") Message m) {
message = m;
}
}
There is a way to add wrapping too; with 1.9 you can use SerializationConfig.Feature.WRAP_ROOT_ELEMENT and DeserializationConfig.Feature.UNWRAP_ROOT_ELEMENT. And if you want to change the wrapper name (by default it is simply unqualified class name), you can use #JsonRootName annotation
Jackson 2.0 adds further dynamic options via ObjectReader and ObjectWriter, as well as JAX-RS annotations.
It was sad to learn that you must write custom serialization for the simple goal of wrapping a class with a labeled object. After playing around with writing a custom serializer, I concluded that the simplest solution is a generic wrapper. Here's perhaps a more simple implementation of your example above:
public final class JsonObjectWrapper {
private JsonObjectWrapper() {}
public static <E> Map<String, E> withLabel(String label, E wrappedObject) {
HashMap<String, E> map = new HashMap<String, E>();
map.put(label, wrappedObject);
return map;
}
}
Provided you don't mind the json having a capital m in message, then the simplest way to do this is to annotate your class with #JsonTypeInfo.
You would add:
#JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class Message {
// ...
}
to get {"Message":{"text":"Text"}}
A Simpler/Better way to do it:
#JsonRootName(value = "message")
public class Message { ...}
then use
new ObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true).writeValueAs...
If using spring, then in application.properties file add following:-
spring.jackson.serialization.WRAP_ROOT_VALUE=true
And then use #JsonRootName annotation on any of your class that you wish to serialize. e.g.
#JsonRootName("user")
public class User {
private String name;
private Integer age;
}
I have created a small jackson module that contains a #JsonWrapped annotation, that solves the problem. See here for the code: https://github.com/mwerlitz/jackson-wrapped
Your class would then look like:
public class Message {
#JsonWrapped("message")
private String text;
}
I am using the Jackson ObjectMapper to deserialize some JSON into a Java class, which we'll call PlayerData. I would like to add a bit of logic to the PlayerData class to fix up some data after the fields have been loaded in. For example, some early JSON files used to use a "sex" flag instead of a "gender" falg, so if the sex flag is set but the gender flag is not set, I'd like to set the value of the gender field to be the value of the sex field.
Is there some sort of #PostConstruct or #AfterLoad annotation that I could affix to a method? Or perhaps an interface that I could implement? I didn't notice one in the documentation, but it seemed like an obvious feature.
Found this thru a link in the comments (credit: fedor.belov). This appears to allow you to run code post construct.
Adding a comment for people who end up here via
http://jira.codehaus.org/browse/JACKSON-645 or
http://jira.codehaus.org/browse/JACKSON-538 and are looking for a
method which is called after a deserializer completes. I was able to
achieve the desired effect by including an annotation and writing a
converter which uses the same class as input and output.
#JsonDeserialize(converter=MyClassSanitizer.class) // invoked after class is fully deserialized
public class MyClass {
public String field1;
}
import com.fasterxml.jackson.databind.util.StdConverter;
public class MyClassSanitizer extends StdConverter<MyClass,MyClass> {
#Override
public MyClass convert(MyClass var1) {
var1.field1 = munge(var1.field1);
return var1;
}
}
If you're not using the #JsonCreator, then Jackson will use the setter and getter methods to set the fields.
So if you define the following methods assuming that you have Sex and Gender enums:
#JsonProperty("sex")
public void setSex(final Sex sex) {
this.sex = sex;
if (gender == null) {
gender = (sex == Sex.WOMAN) ? Gender.WOMAN : Gender.MAN;
}
}
#JsonProperty("gender")
public void setGender(final Gender gender) {
this.gender = gender;
if (sex == null) {
sex = (gender == Gender.WOMAN) ? Sex.WOMAN : Sex.MAN;
}
}
it would work.
Update: You can find all of the annotations of Jackson library here.
Update2: Other solution:
class Example {
private final Sex sex;
private final Gender gender;
#JsonCreator
public Example(#JsonProperty("sex") final Sex sex) {
super();
this.sex = sex;
this.gender = getGenderBySex(sex)
}
#JsonFactory
public static Example createExample(#JsonProperty("gender") final Gender gender) {
return new Example(getSexByGender(gender));
}
private static Sex getSexByGender(final Gender) {
return (gender == Gender.WOMAN) ? Sex.WOMAN : Sex.MAN;
}
private static Gender getGenderBySex(final Sex) {
return (sex == Sex.WOMAN) ? Gender.WOMAN : Gender.MAN;
}
}
This is not supported out of the box, but you can easily create your #JsonPostDeserialize annotation for methods to be called after deserialization.
First, define the annotation:
/**
* Annotation for methods to be called directly after deserialization of the object.
*/
#Target({ ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonPostDeserialize {
}
Then, add the following registration and implementation code to your project:
public static void addPostDeserializeSupport(ObjectMapper objectMapper) {
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDescription,
JsonDeserializer<?> originalDeserializer) {
return new CustomAnnotationsDeserializer(originalDeserializer, beanDescription);
}
});
objectMapper.registerModule(module);
}
/**
* Class implementing the functionality of the {#link JsonPostDeserialize} annotation.
*/
public class CustomAnnotationsDeserializer extends DelegatingDeserializer {
private final BeanDescription beanDescription;
public CustomAnnotationsDeserializer(JsonDeserializer<?> delegate, BeanDescription beanDescription) {
super(delegate);
this.beanDescription = beanDescription;
}
#Override
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
return new CustomAnnotationsDeserializer(newDelegatee, beanDescription);
}
#Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Object deserializedObject = super.deserialize(p, ctxt);
callPostDeserializeMethods(deserializedObject);
return deserializedObject;
}
private void callPostDeserializeMethods(Object deserializedObject) {
for (AnnotatedMethod method : beanDescription.getClassInfo().memberMethods()) {
if (method.hasAnnotation(JsonPostDeserialize.class)) {
try {
method.callOn(deserializedObject);
} catch (Exception e) {
throw new RuntimeException("Failed to call #JsonPostDeserialize annotated method in class "
+ beanDescription.getClassInfo().getName(), e);
}
}
}
}
}
Finally, modify your ObjectMapper instance with addPostDeserializeSupport, it will invoke all #JsonPostDeserialize annotated method of deserialized objects.
This is something that has actually been suggested couple of times earlier. So maybe filing an RFE would make sense; there are multiple ways in which this could work: obvious ones being ability to annotate type (#JsonPostProcess(Processor.class)) and ability to register post-processor through Module API (so that there's basically a callback when Jackson constructs deserializer, to let module specify post-processor to use if any). But perhaps there are even better ways to do this.