-
Notifications
You must be signed in to change notification settings - Fork 4
Reading Validation
nambach edited this page Nov 15, 2021
·
8 revisions
ExcelUtil provides built-in validation APIs that were inspired from Jakarta Bean Validation.
ReaderConfig<Book> readerConfig = ReaderConfig
.fromClass(Book.class)
.titleAtRow(1)
.dataFromRow(2)
// specify validation rules along with reading rules
.column(0, "isbn", v -> v.isString().minLength(10, "ISBN must be at least 20 chars"))
.column(1, "title", v -> v.notNull("Title must not be null"))
.column(2, "author", v -> v.isString().notBlank("Must provide author"))
.column(3, "rating", v -> v.isDecimal().notNull().between(0, 5, "Rating must be between 0 and 5"));
// Read data
Result<Book> books = readerConfig.readSheet(stream);
// Check errors
for (RowError row : books.getErrors()) {
int atRow = row.getExcelIndex(); // row occurs error
String message = row.getInlineMessage(); // summarized error message
System.out.println("Row " + atRow + ": " + message);
// object's detail errors
ObjectError objectError = row.getObjectError();
for (FieldError field : objectError) {
String dtoField = field.getFieldName(); // field name
List<String> dtoFieldErrors = field.getMessages(); // field's errors
}
}
For example, with the below input, we will get these messages
Row 3: 'title': Title must not be null
Row 4: 'isbn': Length must between 10 and 13; 'rating': must not be null
Row 5: 'isbn': Length must between 10 and 13; 'author': Must provide author; 'rating': Rating must be between 0 and 5
If you want to separate validation logic for further re-use, you can do as below.
// Declare independent validation object
Validator<Book> bookValidator = Validator
.fromClass(Book.class)
.on(f -> f.field("isbn")
.validate(v -> v.isString().notNull()
.lengthBetween(10, 13, "Length must between 10 and 13")))
.on(f -> f.field("title")
.validate(v -> v.isString().notBlank("Title must be provided")))
.on(f -> f.field("author")
.validate(v -> v.isString().notBlank("Author must be provided")))
.on(f -> f.field("rating")
.validate(v -> v.isDecimal().notNull()
.between(0, 5, "Rating must be between 0 and 5")));
ReaderConfig<Book> readerConfig = ReaderConfig
.fromClass(Book.class)
.validator(bookValidator) // Then add it to the reader config
...
For more flexible validations, you can specify inside .handler()
and .beforeAddingItem()
as below
ReaderConfig<Book> readerConfig = ReaderConfig
.fromClass(Book.class)
...
.handler(set -> set.atColumn(3)
.handle((book, cell) -> {
Double rating = cell.readDouble();
if (rating == null) {
// set the error, and the row index with column title will be auto added
cell.setError("rating must not be null");
// or throw error and exit the reading process immediately
cell.throwError("rating must not be null");
}
}))
.beforeAddingItem((book, row) -> {
// you can get errors for the current read item
List<RowError> errors = row.getErrors();
// perform extra manual validation here...
// skip item if it is not valid to add
row.skipThisObject();
// or stop immediately to save workload
row.throwError("Stop due to...");
// or simply this
row.terminateNow();
});