Java String validation using enum values and annotation

后端 未结 8 1494
暖寄归人
暖寄归人 2020-11-30 23:47

I want to validate a string against a set of values using annotations.

What I want is basically this:

@ValidateString(enumClass=com.co.enum)
String d         


        
相关标签:
8条回答
  • 2020-12-01 00:01

    Ditch the String representation, and do a real enum.

    public enum DataType {
       STRING,
       BOOLEAN,
       INTEGER;
    }
    

    That way you avoid ever having to do string comparison of the previous String dataType variable to determine if it is in the enum. As an aside, it also makes it impossible to assign a non-valid value to the member variable dataType and since enums are guaranteed to be singletons within the class loader, it also saves on memory footprint.

    It's worth the effort to change your code to use enums. However, assuming that you can't, you can at least change the annotation to use enums.

    @ValidateString(DataType.STRING) String dataType;
    

    and that way your ValidateString annotation at least gets to benefit from enums, even if the rest of the code doesn't.

    Now on the extremely rare chance that you can't use an enumeration at all, you can set static public integers, which map each accepted value.

    public class DataType {
      public static final int STRING = 1;
      public static final int BOOLEAN = 2;
      ...
    }
    

    However, if you use a String for the annotation parameter, we don't have a type checking system which extends into the type to specify that only particular values are allowed. In other words, Java lacks the ability to do something like this:

    public int<values=[1,3,5,7..9]> oddInt; 
    

    which would throw an error if you attempted to assign

     oddInt = 4;
    

    Why is this important? Because if it doesn't apply to regular Java, then it cannot apply to the enumeration which is implemented in regular Java classes.

    0 讨论(0)
  • 2020-12-01 00:01

    Little bit of improvisation with Java 8 Stream API

    import static java.util.stream.Collectors.toList;
    import static java.util.stream.Stream.of;
    import java.util.List;
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, String> 
    {
      private List<String> valueList = null;
      @Override
      public boolean isValid(String value, ConstraintValidatorContext context) {
        return valueList.contains(value.toUpperCase());
      }
      @Override
      public void initialize(EnumValidator constraintAnnotation) {
        valueList = of(constraintAnnotation.enumClazz().getEnumConstants()).map(e->e.toString()).collect(toList());
      }
    }
    
    0 讨论(0)
  • 2020-12-01 00:06

    My attempt for a kotlin one:

    import javax.validation.Constraint
    import javax.validation.ConstraintValidator
    import javax.validation.ConstraintValidatorContext
    import javax.validation.ReportAsSingleViolation
    import javax.validation.constraints.NotNull
    import kotlin.reflect.KClass
    
    @Constraint(validatedBy = [EnumValidatorImpl::class])
    @Retention(AnnotationRetention.RUNTIME)
    @Target(AnnotationTarget.FIELD)
    @NotNull(message = "Value cannot be null")
    @ReportAsSingleViolation
    annotation class EnumValidator(val enumClazz: KClass<*>, val message: String = "Value is not valid")
    
    class EnumValidatorImpl(private var valueList: List<String>? = null) : ConstraintValidator<EnumValidator, String> {
        override fun isValid(value: String?, context: ConstraintValidatorContext?): Boolean =
                valueList?.contains(value?.toUpperCase()) ?: false
    
        override fun initialize(constraintAnnotation: EnumValidator) {
            valueList = constraintAnnotation.enumClazz.java.enumConstants.map { it.toString().toUpperCase() }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 00:09

    Here is a detailed example with the feature of a dynamic error message by Hibernate Documentation

    https://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-customconstraints.html#validator-customconstraints-simple

    0 讨论(0)
  • 2020-12-01 00:16

    I take up Rajeev Singla's response https://stackoverflow.com/a/21070806/8923905, just to optimize the code and allow the String parameter to be null, if in your application it is not mandatory and can be empty :

    1- Remove the @NotNull annotation on the Interface

    2- See the modified code below for the implementation.

    public class EnumValidatorImpl implements ConstraintValidator <EnumValidator,String> {
    
        private List<String> valueList = null;
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            return null == value || valueList.contains(value.toUpperCase());
        }
    
        @Override
        public void initialize(EnumValidator constraintAnnotation) {
            valueList = new ArrayList<>();
            Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClass();
    
            Enum[] enumValArr = enumClass.getEnumConstants();
    
            for(Enum enumVal : enumValArr) {
                valueList.add(enumVal.toString().toUpperCase());
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-12-01 00:21

    This is what I did.

    Annotation

    public @interface ValidateString {
    
        String[] acceptedValues();
    
        String message() default "{uk.dds.ideskos.validator.ValidateString.message}";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { }; 
    }
    

    Validation Class

    public class StringValidator implements ConstraintValidator<ValidateString, String>{
    
        private List<String> valueList;
    
        @Override
        public void initialize(ValidateString constraintAnnotation) {
            valueList = new ArrayList<String>();
            for(String val : constraintAnnotation.acceptedValues()) {
                valueList.add(val.toUpperCase());
            }
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            return valueList.contains(value.toUpperCase());
        }
    
    }
    

    And i used it like

    @ValidateString(acceptedValues={"Integer", "String"}, message="Invalid dataType")
    String dataType;
    
    Long maxValue;
    Long minValue;
    

    Now I need to figure out how to implement conditional check ie. if String then maxValue and minValue should be null or Zero..

    Any ideas?

    0 讨论(0)
提交回复
热议问题