Java Opencsv:允许在使用@PreAssignmentValidator时出现更自定义的错误消息

Java Opencsv:允许在使用@PreAssignmentValidator时出现更自定义的错误消息,java,csv,opencsv,Java,Csv,Opencsv,考虑到这个bean: @Data public class Contact { @PreAssignmentValidator(validator = MustMatchRegexExpression.class, paramString = "^[A-Za-z]{3,8}$") @CsvBindByName(column = "Contact First Name", required = true) private String

考虑到这个bean:

@Data
public class Contact {
    @PreAssignmentValidator(validator = MustMatchRegexExpression.class, paramString = "^[A-Za-z]{3,8}$")
    @CsvBindByName(column = "Contact First Name", required = true)
    private String contactFirstName;

    @PreAssignmentValidator(validator = MustMatchRegexExpression.class, paramString = "^[0-9]{10}$")
    @CsvBindByName(column = "Phone Number", required = true)
    private String phone;
}
CsvToBeanBuilder()的配置如下:

...
final CsvToBean<Contact> beans = new CsvToBeanBuilder<Contact>(
                    Files.newBufferedReader(csvFilePath, StandardCharsets.UTF_8))
                    .withType(Contact.class)
                    .withThrowExceptions(false)
                    .build();

this.contacts = beans.parse();

beans.getCapturedExceptions().stream().forEach(ex -> System.out.println(ex.getMessage()));
...
我(正确)得到以下错误消息:

Field userName value "joe1" did not match expected format of ^[A-Za-z]{3,8}$
Field phone value "123456789" did not match expected format of ^[0-9]{10}$
由于我正在将这些消息传递回用户,因此我更希望错误消息使用CSV列名而不是bean的字段名,并且如果我可以提供自定义验证消息(可能作为@PreAssignmentValidator?的附加字段)使消息看起来像:

Field 'User Name' value "joe1" did not match expected format of '3 - 8 alphabetic characters'
Field 'Phone Number' value "123456789" did not match expected format of '10 digits'
关于如何在不编写一些自定义逻辑来转换这些消息的情况下做到这一点,有什么建议/指针吗

谢谢大家!


根据@andrewjames的建议更新代码

@Getter
public class Contact {
    //@PreAssignmentValidator(validator = MustMatchRegexExpression.class, paramString = "^[A-Za-z]{3,8}$")
    @CsvBindByName(column = "Contact First Name", required = true)
    private String contactFirstName;

    //@PreAssignmentValidator(validator = MustMatchRegexExpression.class, paramString = "^[0-9]{10}$")
    @CsvBindByName(column = "Phone Number", required = true)
    private String phone;

    public void setContactFirstName(String contactFirstName) throws CsvValidationException {
        if (contactFirstName.length() < 3 || contactFirstName.length() > 8) {
            throw new CsvValidationException("'Contact First Name' must be between 3-8 characters long");
        }
        this.contactFirstName = contactFirstName;
    }

    public void setPhone(String phone) throws CsvValidationException {
        if (phone.length() != 10) {
            throw new CsvValidationException("'Phone Number' must be between 10 digits long");
        }
        this.phone = phone;
    }
}

public class ContactTest {
    private static final String HEADER = "Contact First Name,Phone Number\n";


    @Test
    public void test() throws Exception {
        String data = HEADER
                + "jo,1234567890\n"
                + "al,123456789";  // This row should generate two exceptions

        CsvToBean<Contact> csvToBean = new CsvToBeanBuilder<Contact>(new StringReader(data))
                .withType(Contact.class)
                .withThrowExceptions(false)
//                .withExceptionHandler(new ExceptionHandlerQueue())  // Tried this way after commenting previous line
                .build();

        List<Contact> beans = csvToBean.parse();

        csvToBean.getCapturedExceptions().stream().forEach((ex) -> {
                System.out.println((int) ex.getLineNumber() + " -- " + ex.getMessage());
        });
    }
}

Hibernate的验证是在转换后进行的,这意味着您必须首先将数据从CSV读取到目标
联系人
bean列表中

但是,您仍然可以系统地捕获所有验证消息,并且可以自定义它们以满足您的特定需求

首先,您需要一些额外的库:

  • Hibernate验证器(及其依赖项)
  • Glassfish的EL(表达式语言)处理器
我使用Maven实现以下功能:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.1-b11</version>
</dependency>
注释:

@Pattern(regexp="^[A-Za-z]{3,8}$", 
        message="The value '${validatedValue}' in the 'Contact First Name' column did not match the expected format of 3 to 8 letters.")
@CsvBindByName(column = "Contact First Name", required = true)
private String contactFirstName;

@Pattern(regexp="^[0-9]{10}$", message="Another custom message...")
@CsvBindByName(column = "Phone Number", required = true)
private String phone;
处理CSV文件时,您的逻辑与原始问题中的逻辑相同。但是,您可以使用Hibernate,而不是使用
bean.getCapturedExceptions()

需要以下进口:

import javax.validation.ValidatorFactory;
import javax.validation.Validator;
import javax.validation.Validation;
import javax.validation.ConstraintViolation;
Hibernate提供了这些应用程序所需的实现类

主要逻辑(适用于我的测试用例):

您将打印以下错误消息:

“联系人姓名”列中的值“joe9”与预期的3到8个字母格式不匹配


要明确的是,这不是在CSV解析期间发生的,而是在之后作为一个单独的步骤发生的。根据我的经验,这是使用Hibernate进行基本验证的典型方式。

在使用
预分配验证器时,没有提供自定义消息的选项。但是,您可以使用任何标准验证库(例如)来执行转换后验证。例如,Hibernate支持自定义参数化消息。@andrewjames感谢您的建议。我仍在努力寻找如何让它以我希望的方式工作。我向setter添加了一些简单的验证,如果验证失败,则抛出CsvValidationException。但这不是我想要的。我将用我所做的更新我的样本。如果你能发布一个示例,那将非常有帮助。你是要求基于你的方法(我无法做到)的示例还是基于Hibernate的示例?@andrewjames如果Hibernate方法可以显示如何在解析过程中捕获验证错误,然后在解析结束时列出这些错误,那真的很有帮助!
import javax.validation.constraints.Pattern;
@Pattern(regexp="^[A-Za-z]{3,8}$", 
        message="The value '${validatedValue}' in the 'Contact First Name' column did not match the expected format of 3 to 8 letters.")
@CsvBindByName(column = "Contact First Name", required = true)
private String contactFirstName;

@Pattern(regexp="^[0-9]{10}$", message="Another custom message...")
@CsvBindByName(column = "Phone Number", required = true)
private String phone;
import javax.validation.ValidatorFactory;
import javax.validation.Validator;
import javax.validation.Validation;
import javax.validation.ConstraintViolation;
Path csvFilePath = Paths.get("C:/tmp/csv/test_01.csv");
final CsvToBean<Contact> beans = new CsvToBeanBuilder<Contact>(
        Files.newBufferedReader(csvFilePath, StandardCharsets.UTF_8))
        .withType(Contact.class)
        .withThrowExceptions(false)
        .build();

List<Contact> contacts = beans.parse();

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

for (Contact contact : contacts) {
    Set<ConstraintViolation<Contact>> violations = validator.validate(contact);
    for (ConstraintViolation<Contact> violation : violations) {
        System.out.println(violation.getMessage());
    }
}
joe9,123456789