基元集合的 Hibernate 验证

发布于 2024-10-04 15:46:58 字数 206 浏览 9 评论 0 原文

我希望能够执行以下操作:

@Email
public List<String> getEmailAddresses()
{
   return this.emailAddresses;
}

换句话说,我希望列表中的每个项目都被验证为电子邮件地址。当然,像这样注释集合是不可接受的。

有办法做到这一点吗?

I want to be able to do something like:

@Email
public List<String> getEmailAddresses()
{
   return this.emailAddresses;
}

In other words, I want each item in the list to be validated as an email address. Of course, it is not acceptable to annotate a collection like this.

Is there a way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

鼻尖触碰 2024-10-11 15:46:58

JSR-303 和 Hibernate Validator 都没有任何现成的约束来验证 Collection 的每个元素。

解决此问题的一种可能的解决方案是创建自定义 @ValidCollection 约束和相应的验证器实现 ValidCollectionValidator

为了验证集合的每个元素,我们需要在 ValidCollectionValidator 内有一个 Validator 实例;为了获得这样的实例,我们需要自定义实现ConstraintValidatorFactory。

看看您是否喜欢以下解决方案...

简单地,

  • 复制粘贴所有这些 java 类(并导入相关类);
  • 在类路径上添加validation-api、hibenate-validator、slf4j-log4j12和testng jar;
  • 运行测试用例。

ValidCollection

    public @interface ValidCollection {

    Class<?> elementType();

    /* Specify constraints when collection element type is NOT constrained 
     * validator.getConstraintsForClass(elementType).isBeanConstrained(); */
    Class<?>[] constraints() default {};

    boolean allViolationMessages() default true;

    String message() default "{ValidCollection.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

ValidCollectionValidator

    public class ValidCollectionValidator implements ConstraintValidator<ValidCollection, Collection>, ValidatorContextAwareConstraintValidator {

    private static final Logger logger = LoggerFactory.getLogger(ValidCollectionValidator.class);

    private ValidatorContext validatorContext;

    private Class<?> elementType;
    private Class<?>[] constraints;
    private boolean allViolationMessages;

    @Override
    public void setValidatorContext(ValidatorContext validatorContext) {
        this.validatorContext = validatorContext;
    }

    @Override
    public void initialize(ValidCollection constraintAnnotation) {
        elementType = constraintAnnotation.elementType();
        constraints = constraintAnnotation.constraints();
        allViolationMessages = constraintAnnotation.allViolationMessages();
    }

    @Override
    public boolean isValid(Collection collection, ConstraintValidatorContext context) {
        boolean valid = true;

        if(collection == null) {
            //null collection cannot be validated
            return false;
        }

        Validator validator = validatorContext.getValidator();

        boolean beanConstrained = validator.getConstraintsForClass(elementType).isBeanConstrained();

        for(Object element : collection) {
            Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>> ();

            if(beanConstrained) {
                boolean hasValidCollectionConstraint = hasValidCollectionConstraint(elementType);
                if(hasValidCollectionConstraint) {
                    // elementType has @ValidCollection constraint
                    violations.addAll(validator.validate(element));
                } else {
                    violations.addAll(validator.validate(element));
                }
            } else {
                for(Class<?> constraint : constraints) {
                    String propertyName = constraint.getSimpleName();
                    propertyName = Introspector.decapitalize(propertyName);
                    violations.addAll(validator.validateValue(CollectionElementBean.class, propertyName, element));
                }
            }

            if(!violations.isEmpty()) {
                valid = false;
            }

            if(allViolationMessages) { //TODO improve
                for(ConstraintViolation<?> violation : violations) {
                    logger.debug(violation.getMessage());
                    ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(violation.getMessage());
                    violationBuilder.addConstraintViolation();
                }
            }

        }

        return valid;
    }

    private boolean hasValidCollectionConstraint(Class<?> beanType) {
        BeanDescriptor beanDescriptor = validatorContext.getValidator().getConstraintsForClass(beanType);
        boolean isBeanConstrained = beanDescriptor.isBeanConstrained();
        if(!isBeanConstrained) {
            return false;
        }
        Set<ConstraintDescriptor<?>> constraintDescriptors = beanDescriptor.getConstraintDescriptors(); 
        for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
            if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
                return true;
            }
        }
        Set<PropertyDescriptor> propertyDescriptors = beanDescriptor.getConstrainedProperties();
        for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
            for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
                if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
                    return true;
                }
            }    
        }
        return false;
    }

}

ValidatorContextAwareConstraintValidator

public interface ValidatorContextAwareConstraintValidator {

    void setValidatorContext(ValidatorContext validatorContext);

}

CollectionElementBean

    public class CollectionElementBean {

    /* add more properties on-demand */
    private Object notNull;
    private String notBlank;
    private String email;

    protected CollectionElementBean() {
    }

    @NotNull
    public Object getNotNull() { return notNull; }
    public void setNotNull(Object notNull) { this.notNull = notNull; }

    @NotBlank
    public String getNotBlank() { return notBlank; }
    public void setNotBlank(String notBlank) { this.notBlank = notBlank; }

    @Email
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

}

ConstraintValidatorFactoryImpl

public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {

    private ValidatorContext validatorContext;

    public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) {
        this.validatorContext = nativeValidator;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        T instance = null;

        try {
            instance = key.newInstance();
        } catch (Exception e) { 
            // could not instantiate class
            e.printStackTrace();
        }

        if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) {
            ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance;
            validator.setValidatorContext(validatorContext);
        }

        return instance;
    }

}

员工

public class Employee {

    private String firstName;
    private String lastName;
    private List<String> emailAddresses;

    @NotNull
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    @ValidCollection(elementType=String.class, constraints={Email.class})
    public List<String> getEmailAddresses() { return emailAddresses; }
    public void setEmailAddresses(List<String> emailAddresses) { this.emailAddresses = emailAddresses; }

}

团队

public class Team {

    private String name;
    private Set<Employee> members;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @ValidCollection(elementType=Employee.class)
    public Set<Employee> getMembers() { return members; }
    public void setMembers(Set<Employee> members) { this.members = members; }

}

ShoppingCart

public class ShoppingCart {

    private List<String> items;

    @ValidCollection(elementType=String.class, constraints={NotBlank.class})
    public List<String> getItems() { return items; }
    public void setItems(List<String> items) { this.items = items; }

}

ValidCollectionTest

public class ValidCollectionTest {

    private static final Logger logger = LoggerFactory.getLogger(ValidCollectionTest.class);

    private ValidatorFactory validatorFactory;

    @BeforeClass
    public void createValidatorFactory() {
        validatorFactory = Validation.buildDefaultValidatorFactory();
    }

    private Validator getValidator() {
        ValidatorContext validatorContext = validatorFactory.usingContext();
        validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext));
        Validator validator = validatorContext.getValidator();
        return validator;
    }

    @Test
    public void beanConstrained() {
        Employee se = new Employee();
        se.setFirstName("Santiago");
        se.setLastName("Ennis");
        se.setEmailAddresses(new ArrayList<String> ());
        se.getEmailAddresses().add("segmail.com");
        Employee me = new Employee();
        me.setEmailAddresses(new ArrayList<String> ());
        me.getEmailAddresses().add("[email protected]");

        Team team = new Team();
        team.setMembers(new HashSet<Employee>());
        team.getMembers().add(se);
        team.getMembers().add(me);

        Validator validator = getValidator();

        Set<ConstraintViolation<Team>> violations = validator.validate(team);
        for(ConstraintViolation<Team> violation : violations) {
            logger.info(violation.getMessage());
        }
    }

    @Test
    public void beanNotConstrained() {
        ShoppingCart cart = new ShoppingCart();
        cart.setItems(new ArrayList<String> ());
        cart.getItems().add("JSR-303 Book");
        cart.getItems().add("");

        Validator validator = getValidator();

        Set<ConstraintViolation<ShoppingCart>> violations = validator.validate(cart, Default.class);
        for(ConstraintViolation<ShoppingCart> violation : violations) {
            logger.info(violation.getMessage());
        }
    }

}

输出

02:16:37,581  INFO main validation.ValidCollectionTest:66 - {ValidCollection.message}
02:16:38,303  INFO main validation.ValidCollectionTest:66 - may not be null
02:16:39,092  INFO main validation.ValidCollectionTest:66 - not a well-formed email address

02:17:46,460  INFO main validation.ValidCollectionTest:81 - may not be empty
02:17:47,064  INFO main validation.ValidCollectionTest:81 - {ValidCollection.message}

注意:- 当bean有约束时,不要指定@ValidCollection 约束的 >constraints 属性。当 bean 没有约束时,constraints 属性是必需的。

Neither JSR-303 nor Hibernate Validator has any ready-made constraint that can validate each elements of Collection.

One possible solution to address this issue is to create a custom @ValidCollection constraint and corresponding validator implementation ValidCollectionValidator.

To validate each element of collection we need an instance of Validator inside ValidCollectionValidator; and to get such instance we need custom implementation of ConstraintValidatorFactory.

See if you like following solution...

Simply,

  • copy-paste all these java classes (and import relavent classes);
  • add validation-api, hibenate-validator, slf4j-log4j12, and testng jars on classpath;
  • run the test-case.

ValidCollection

    public @interface ValidCollection {

    Class<?> elementType();

    /* Specify constraints when collection element type is NOT constrained 
     * validator.getConstraintsForClass(elementType).isBeanConstrained(); */
    Class<?>[] constraints() default {};

    boolean allViolationMessages() default true;

    String message() default "{ValidCollection.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

ValidCollectionValidator

    public class ValidCollectionValidator implements ConstraintValidator<ValidCollection, Collection>, ValidatorContextAwareConstraintValidator {

    private static final Logger logger = LoggerFactory.getLogger(ValidCollectionValidator.class);

    private ValidatorContext validatorContext;

    private Class<?> elementType;
    private Class<?>[] constraints;
    private boolean allViolationMessages;

    @Override
    public void setValidatorContext(ValidatorContext validatorContext) {
        this.validatorContext = validatorContext;
    }

    @Override
    public void initialize(ValidCollection constraintAnnotation) {
        elementType = constraintAnnotation.elementType();
        constraints = constraintAnnotation.constraints();
        allViolationMessages = constraintAnnotation.allViolationMessages();
    }

    @Override
    public boolean isValid(Collection collection, ConstraintValidatorContext context) {
        boolean valid = true;

        if(collection == null) {
            //null collection cannot be validated
            return false;
        }

        Validator validator = validatorContext.getValidator();

        boolean beanConstrained = validator.getConstraintsForClass(elementType).isBeanConstrained();

        for(Object element : collection) {
            Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>> ();

            if(beanConstrained) {
                boolean hasValidCollectionConstraint = hasValidCollectionConstraint(elementType);
                if(hasValidCollectionConstraint) {
                    // elementType has @ValidCollection constraint
                    violations.addAll(validator.validate(element));
                } else {
                    violations.addAll(validator.validate(element));
                }
            } else {
                for(Class<?> constraint : constraints) {
                    String propertyName = constraint.getSimpleName();
                    propertyName = Introspector.decapitalize(propertyName);
                    violations.addAll(validator.validateValue(CollectionElementBean.class, propertyName, element));
                }
            }

            if(!violations.isEmpty()) {
                valid = false;
            }

            if(allViolationMessages) { //TODO improve
                for(ConstraintViolation<?> violation : violations) {
                    logger.debug(violation.getMessage());
                    ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(violation.getMessage());
                    violationBuilder.addConstraintViolation();
                }
            }

        }

        return valid;
    }

    private boolean hasValidCollectionConstraint(Class<?> beanType) {
        BeanDescriptor beanDescriptor = validatorContext.getValidator().getConstraintsForClass(beanType);
        boolean isBeanConstrained = beanDescriptor.isBeanConstrained();
        if(!isBeanConstrained) {
            return false;
        }
        Set<ConstraintDescriptor<?>> constraintDescriptors = beanDescriptor.getConstraintDescriptors(); 
        for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
            if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
                return true;
            }
        }
        Set<PropertyDescriptor> propertyDescriptors = beanDescriptor.getConstrainedProperties();
        for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
            for(ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
                if(constraintDescriptor.getAnnotation().annotationType().getName().equals(ValidCollection.class.getName())) {
                    return true;
                }
            }    
        }
        return false;
    }

}

ValidatorContextAwareConstraintValidator

public interface ValidatorContextAwareConstraintValidator {

    void setValidatorContext(ValidatorContext validatorContext);

}

CollectionElementBean

    public class CollectionElementBean {

    /* add more properties on-demand */
    private Object notNull;
    private String notBlank;
    private String email;

    protected CollectionElementBean() {
    }

    @NotNull
    public Object getNotNull() { return notNull; }
    public void setNotNull(Object notNull) { this.notNull = notNull; }

    @NotBlank
    public String getNotBlank() { return notBlank; }
    public void setNotBlank(String notBlank) { this.notBlank = notBlank; }

    @Email
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

}

ConstraintValidatorFactoryImpl

public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {

    private ValidatorContext validatorContext;

    public ConstraintValidatorFactoryImpl(ValidatorContext nativeValidator) {
        this.validatorContext = nativeValidator;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        T instance = null;

        try {
            instance = key.newInstance();
        } catch (Exception e) { 
            // could not instantiate class
            e.printStackTrace();
        }

        if(ValidatorContextAwareConstraintValidator.class.isAssignableFrom(key)) {
            ValidatorContextAwareConstraintValidator validator = (ValidatorContextAwareConstraintValidator) instance;
            validator.setValidatorContext(validatorContext);
        }

        return instance;
    }

}

Employee

public class Employee {

    private String firstName;
    private String lastName;
    private List<String> emailAddresses;

    @NotNull
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    @ValidCollection(elementType=String.class, constraints={Email.class})
    public List<String> getEmailAddresses() { return emailAddresses; }
    public void setEmailAddresses(List<String> emailAddresses) { this.emailAddresses = emailAddresses; }

}

Team

public class Team {

    private String name;
    private Set<Employee> members;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @ValidCollection(elementType=Employee.class)
    public Set<Employee> getMembers() { return members; }
    public void setMembers(Set<Employee> members) { this.members = members; }

}

ShoppingCart

public class ShoppingCart {

    private List<String> items;

    @ValidCollection(elementType=String.class, constraints={NotBlank.class})
    public List<String> getItems() { return items; }
    public void setItems(List<String> items) { this.items = items; }

}

ValidCollectionTest

public class ValidCollectionTest {

    private static final Logger logger = LoggerFactory.getLogger(ValidCollectionTest.class);

    private ValidatorFactory validatorFactory;

    @BeforeClass
    public void createValidatorFactory() {
        validatorFactory = Validation.buildDefaultValidatorFactory();
    }

    private Validator getValidator() {
        ValidatorContext validatorContext = validatorFactory.usingContext();
        validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(validatorContext));
        Validator validator = validatorContext.getValidator();
        return validator;
    }

    @Test
    public void beanConstrained() {
        Employee se = new Employee();
        se.setFirstName("Santiago");
        se.setLastName("Ennis");
        se.setEmailAddresses(new ArrayList<String> ());
        se.getEmailAddresses().add("segmail.com");
        Employee me = new Employee();
        me.setEmailAddresses(new ArrayList<String> ());
        me.getEmailAddresses().add("[email protected]");

        Team team = new Team();
        team.setMembers(new HashSet<Employee>());
        team.getMembers().add(se);
        team.getMembers().add(me);

        Validator validator = getValidator();

        Set<ConstraintViolation<Team>> violations = validator.validate(team);
        for(ConstraintViolation<Team> violation : violations) {
            logger.info(violation.getMessage());
        }
    }

    @Test
    public void beanNotConstrained() {
        ShoppingCart cart = new ShoppingCart();
        cart.setItems(new ArrayList<String> ());
        cart.getItems().add("JSR-303 Book");
        cart.getItems().add("");

        Validator validator = getValidator();

        Set<ConstraintViolation<ShoppingCart>> violations = validator.validate(cart, Default.class);
        for(ConstraintViolation<ShoppingCart> violation : violations) {
            logger.info(violation.getMessage());
        }
    }

}

Output

02:16:37,581  INFO main validation.ValidCollectionTest:66 - {ValidCollection.message}
02:16:38,303  INFO main validation.ValidCollectionTest:66 - may not be null
02:16:39,092  INFO main validation.ValidCollectionTest:66 - not a well-formed email address

02:17:46,460  INFO main validation.ValidCollectionTest:81 - may not be empty
02:17:47,064  INFO main validation.ValidCollectionTest:81 - {ValidCollection.message}

Note:- When bean has constraints do NOT specify the constraints attribute of @ValidCollection constraint. The constraints attribute is necessary when bean has no constraint.

九命猫 2024-10-11 15:46:58

我没有足够高的声誉来对原始答案发表评论,但也许值得注意的是这个问题 JSR-308 正处于最终发布阶段,发布后将解决此问题!不过,它至少需要 Java 8。

唯一的区别是验证注释将位于类型声明内部。

//@Email
public List<@Email String> getEmailAddresses()
{
   return this.emailAddresses;
}

请告诉我您认为我可以最好地将这些信息放在哪里,以供其他正在寻找的人使用。谢谢!

PS:如需了解更多信息,请查看这篇文章

I don't have a high enough reputation to comment this on the original answer, but perhaps it is worth noting on this question that JSR-308 is in its final release stage and will address this problem when it is released! It will at least require Java 8, however.

The only difference would be that the validation annotation would go inside the type declaration.

//@Email
public List<@Email String> getEmailAddresses()
{
   return this.emailAddresses;
}

Please let me know where you think I could best put this information for others who are looking. Thanks!

P.S. For more info, check out this SO post.

动听の歌 2024-10-11 15:46:58

由于 Java 注解本身的限制,不可能编写像 @EachElement 这样的通用包装器注解来包装任何约束注解。但是,您可以编写一个通用约束验证器类,它将每个元素的实际验证委托给现有的约束验证器。您必须为每个约束编写一个包装器注释,但只需一个验证器。

我已经在 jirutka/validator-collection (可在 Maven Central 中找到)中实现了这种方法。例如:

@EachSize(min = 5, max = 255)
List<String> values;

该库允许您轻松地为任何验证约束创建“伪约束”来注释简单类型的集合,而无需为每个集合编写额外的验证器或不必要的包装类。所有标准 Bean 验证约束和 Hibernate 特定约束都支持 EachX 约束。

要为您自己的 @Awesome 约束创建 @EachAwesome,只需复制并粘贴注释类,将 @Constraint 注释替换为 @ Constraint(validatedBy = CommonEachValidator.class) 并添加注释 @EachConstraint(validateAs = Awesome.class)。就这样!

// common boilerplate
@Documented
@Retention(RUNTIME)
@Target({METHOD, FIELD, ANNOTATION_TYPE})
// this is important!
@EachConstraint(validateAs = Awesome.class)
@Constraint(validatedBy = CommonEachValidator.class)
public @interface EachAwesome {

    // copy&paste all attributes from Awesome annotation here
    String message() default "";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String someAttribute();
}

编辑:针对当前版本的库进行了更新。

It’s not possible to write a generic wrapper annotation like @EachElement to wrap any constraint annotation — due to limitations of Java Annotations itself. However, you can write a generic constraint validator class which delegates actual validation of every element to an existing constraint validator. You have to write a wrapper annotation for every constraint, but just one validator.

I’ve implemented this approach in jirutka/validator-collection (available in Maven Central). For example:

@EachSize(min = 5, max = 255)
List<String> values;

This library allows you to easily create a “pseudo constraint” for any validation constraint to annotate a collection of simple types, without writing an extra validator or unnecessary wrapper classes for every collection. EachX constraint is supported for all standard Bean Validation constraints and Hibernate specific constraints.

To create an @EachAwesome for your own @Awesome constraint, just copy&paste the annotation class, replace @Constraint annotation with @Constraint(validatedBy = CommonEachValidator.class) and add the annotation @EachConstraint(validateAs = Awesome.class). That’s all!

// common boilerplate
@Documented
@Retention(RUNTIME)
@Target({METHOD, FIELD, ANNOTATION_TYPE})
// this is important!
@EachConstraint(validateAs = Awesome.class)
@Constraint(validatedBy = CommonEachValidator.class)
public @interface EachAwesome {

    // copy&paste all attributes from Awesome annotation here
    String message() default "";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String someAttribute();
}

EDIT: Updated for the current version of library.

你在我安 2024-10-11 15:46:58

感谢 becomputer06 的精彩回答。
但我认为应该将以下注释添加到 ValidCollection 定义中:

@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidCollectionValidator.class)

而且我仍然不明白如何处理原始类型包装器和约束注释(如 @Size、@Min、@Max 等)的集合,因为值不能通过becomputer06的方式。

当然,我可以为应用程序中的所有情况创建自定义约束注释,但无论如何我必须将这些注释的属性添加到 CollectionElementBean。这似乎是一个足够糟糕的解决方案。

Thanks for great answer from becomputer06.
But I think the following annotations should be added to ValidCollection definition:

@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ValidCollectionValidator.class)

And I still don't understant what to do with collections of primitive type wrappers and constrains annotations like @Size, @Min, @Max etc., because value can't be passed through becomputer06's way.

Of course, I can create custom contraint annotations for all cases in my application, but anyway I have to add properties for these annotations to CollectionElementBean. And it seems to be a bad enough solution.

凑诗 2024-10-11 15:46:58

JSR-303 能够扩展内置约束的目标类型:请参阅 7.1.2 。覆盖 XML 中的约束定义

您可以实现一个 ConstraintValidator> ,它与给定的答案执行相同的操作,委托给原始验证器。然后,您可以保留模型定义并在 List 上应用 @Email

JSR-303 has the ability to extend the target types of built in constraints: See 7.1.2. Overriding constraint definitions in XML.

You can implement a ConstraintValidator<Email, List<String>> which does the same thing as the given answers, delegating to the primitive validator. Then you can keep your model definition and apply @Email on List<String>.

尝蛊 2024-10-11 15:46:58

可以采用非常简单的解决方法。您可以改为验证包装简单值属性的类的集合。为此,您需要在集合上使用 @Valid 注释。

示例:

public class EmailAddress {

  @Email
  String email;

  public EmailAddress(String email){
    this.email = email;
  }
}

public class Foo {

  /* Validation that works */
  @Valid
  List<EmailAddress> getEmailAddresses(){
    return this.emails.stream().map(EmailAddress::new).collect(toList());
  }

}

A very simple workaround is possible. You can instead validate a collection of your classes that wrap the simple value property. For this to work you need to use @Valid annotation on the collection.

Example:

public class EmailAddress {

  @Email
  String email;

  public EmailAddress(String email){
    this.email = email;
  }
}

public class Foo {

  /* Validation that works */
  @Valid
  List<EmailAddress> getEmailAddresses(){
    return this.emails.stream().map(EmailAddress::new).collect(toList());
  }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文