OpenCSV多字段验证问题:单字段校验失败致后续字段未校验
解决OpenCSV BeanVerifier多字段校验不完整问题
要实现CSV中COD_NATURE和COD_TAXATION字段的全量校验,且每个错误字段都能通过csvToBean.getCapturedExceptions()获取到对应的CsvConstraintViolationException,核心是自定义一个全局BeanVerifier,批量收集所有字段的校验错误后统一抛出,避免单个错误终止校验。
实现步骤
1. 自定义全局BeanVerifier
创建一个包含所有校验逻辑的Verifier,在其中逐一检查字段,收集所有违反约束的情况,最后一次性抛出包含所有错误的异常。
import com.opencsv.bean.BeanVerifier; import com.opencsv.exceptions.CsvConstraintViolationException; import com.opencsv.exceptions.CsvValidationException; import java.util.ArrayList; import java.util.List; public class DatabaseFieldVerifier<T> implements BeanVerifier<T> { // 注入数据库查询依赖(根据你的持久化框架调整,比如JdbcTemplate、Spring Data Repository) private final NatureDao natureDao; private final TaxationDao taxationDao; public DatabaseFieldVerifier(NatureDao natureDao, TaxationDao taxationDao) { this.natureDao = natureDao; this.taxationDao = taxationDao; } @Override public boolean verify(T bean) throws CsvValidationException { List<CsvConstraintViolation> violations = new ArrayList<>(); YourCsvBean csvBean = (YourCsvBean) bean; long lineNumber = csvBean.getLineNumber(); // 确保Bean持有行号字段 // 校验COD_NATURE存在性 String codNature = csvBean.getCodNature(); if (codNature != null && !natureDao.exists(codNature)) { violations.add(new CsvConstraintViolation( "COD_NATURE", codNature, "该值不存在于数据库中", lineNumber )); } // 校验COD_TAXATION存在性 String codTaxation = csvBean.getCodTaxation(); if (codTaxation != null && !taxationDao.exists(codTaxation)) { violations.add(new CsvConstraintViolation( "COD_TAXATION", codTaxation, "该值不存在于数据库中", lineNumber )); } // 若有错误则统一抛出 if (!violations.isEmpty()) { throw new CsvConstraintViolationException(violations); } return true; } }
2. 配置CsvToBean并启用异常捕获
配置时仅添加上述自定义Verifier,并设置withThrowExceptions(false)让OpenCSV捕获所有异常而非终止解析。
// 假设已创建CSV阅读器 Reader reader = new FileReader("your-file.csv"); CsvToBeanBuilder<YourCsvBean> builder = new CsvToBeanBuilder<>(reader) .withType(YourCsvBean.class) .withVerifier(new DatabaseFieldVerifier(natureDao, taxationDao)) .withThrowExceptions(false) // 关键:捕获异常而非终止 .withLineNumber(true); // 自动注入行号到Bean的lineNumber字段 CsvToBean<YourCsvBean> csvToBean = builder.build(); List<YourCsvBean> validBeans = csvToBean.parse(); // 获取所有捕获的异常并处理 List<CsvException> exceptions = csvToBean.getCapturedExceptions(); for (CsvException e : exceptions) { if (e instanceof CsvConstraintViolationException) { CsvConstraintViolationException violationEx = (CsvConstraintViolationException) e; for (CsvConstraintViolation violation : violationEx.getConstraintViolations()) { // 输出或记录错误信息 System.out.printf("行号%d | 字段%s | 错误:%s%n", violation.getLineNumber(), violation.getField(), violation.getMessage()); } } }
3. 确保CSV Bean包含行号字段
在你的CSV映射Bean中添加行号字段,用于错误定位:
public class YourCsvBean { private long lineNumber; private String codNature; private String codTaxation; // Getter和Setter public long getLineNumber() { return lineNumber; } public void setLineNumber(long lineNumber) { this.lineNumber = lineNumber; } public String getCodNature() { return codNature; } public void setCodNature(String codNature) { this.codNature = codNature; } public String getCodTaxation() { return codTaxation; } public void setCodTaxation(String codTaxation) { this.codTaxation = codTaxation; } }
关键说明
- 不要配置多个BeanVerifier:多个Verifier会按顺序执行,第一个错误就会终止当前行的校验,必须将所有逻辑集中到一个Verifier中。
withThrowExceptions(false)是核心:开启后OpenCSV会将每行的异常捕获并存储,而非直接抛出中断解析。CsvConstraintViolationException可携带多个错误:通过该异常的getConstraintViolations()方法能获取当前行所有字段的错误信息,满足你“每个错误字段对应抛出异常”的需求(所有错误会被封装在同一个异常实例中,遍历即可获取每个字段的错误详情)。
内容的提问来源于stack exchange,提问作者Matheus Hoffmann
相关产品推荐
相关产品推荐

