Skip to content

[Enhancement] Introduce standard Bean Validation API support for excel reading #741

@bengbengbalabalabeng

Description

@bengbengbalabalabeng

Search before asking

I have read and referred to similar issues in the issues .

  • I searched in the issues and found nothing similar.

Motivation

This enhancement aims to integrate the Bean Validation API into the Fesod reading lifecycle, allowing users to define validation rules declaratively using annotations on their POJOs. This will eliminate the need for redundant and complex validation logic—such as long chains of if-else statements—within ReadListener implementations, resulting in a more robust and maintainable data validation solution.

Solution

To provide comprehensive support for Bean Validation, we must address the namespace change from javax.validation to jakarta.validation that occurred after Java 8. Given that Fesod supports a wide range of JDKs (8 through 25), a flexible, multi-module approach is required.

Content

  1. A Validator SPI (Service Provider Interface) will be defined within the main fesod module. This ensures the core library remains decoupled from any specific validation API.
  2. Create two separate adapter modules:
  • fesod-bval will adapt Fesod's SPI to the jakarta.validation API.
  • fesod-bval-javax will be created by duplicating fesod-bval and modifying the imports to support the legacy javax.validation API.
  1. The adapter modules will only depend on the standard Bean Validation APIs. The user is free to choose and provide any compliant implementation (e.g., Hibernate Validator, Apache BVal).
graph TD
    app["Application"]

    subgraph "SPI Definition"
        A["fesod"]
    end

    subgraph " "
        B["fesod-bval (for jakarta)"]
        C["fesod-bval-javax (for javax)"]
    end
    
    subgraph " "
        D["hibernate-validator"]
        E["apache bval-jsr"]
        F["others"]
    end

    A --> B
    A --> C
    app -.-> A
    app -.-> adapters{"Choose One SPI Implementation"}
    adapters -.-> B
    adapters -.-> C
    app -.-> beanvalidation{"Choose One Bean Validation Implementation"}
    beanvalidation -.-> D
    beanvalidation -.-> E
    beanvalidation -.-> F
Loading

API Usage Example

1. Add Dependencies

<dependency>
    <groupId>org.apache.fesod</groupId>
    <artifactId>fesod</artifactId>
    <version>${fesod.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.fesod</groupId>
    <artifactId>fesod-bval</artifactId>
    <version>${fesod.version}</version>
</dependency>

<!-- Users provide their chosen Bean Validation implementation, e.g., Hibernate Validator, Apache BVal -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate-validator.version}</version>
</dependency>
<!-- EL Support -->
<dependency>
    <groupId>org.glassfish.expressly</groupId>
    <artifactId>expressly</artifactId>
    <version>${expressly.version}</version>
</dependency>

2. Define the POJO with Validation Annotations

public class ExcelModel {
	
	@NotBlank(message = "In sheet '{sheetName}', row {rownum}, column {column}: Name is required")
	@ExcelProperty("Name")
	private String name;

	@Min(value = 20, message = "Age must be greater than or equal to {value}")
	@ExcelProperty("Age")
	private Integer age;
}

3. Read the Excel File (Async)

File file = new File("file.xlsx");

Fesod.read(file, ExcelModel.class, new CustomAnalysisEventListener())
     .useValidation(true)
     .sheet()
     .doRead();

The ValidatingAnalysisEventListener provides precise error information through its callback methods. After the entire analysis is complete, a comprehensive Errors object containing all validation failures can be retrieved via a new convenience method on the AnalysisContext. The Errors object contains a detailed list of RowError (for row-level validation) and PropertyError (for cell-level validation) objects.

public class CustomAnalysisEventListener extends ValidatingAnalysisEventListener<ExcelModel> {
	
	@Override
	public void onValidated(ExcelModel data, AnalysisContext context) {
		// Called for each row that successfully passes validation.
	}

	@Override
    public void onValidated(ExcelModel data, List<RowError> validationErrors, AnalysisContext context) {
    	// Called for each row that fails validation.
    	// Each error in validationErrors contains detailed context like sheetNo, sheetName, rownum...
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
    	 // Retrieve the full report of all errors
    	 Errors validatedErrors = context.getValidationErrors();
    	 if (validatedErrors.hasErrors) {
    	 	// ...
    	 }
    }
}

4. Support Fesod-Specific Parameters Expression (such as {sheetNo}, {sheetName}, {rownum}, and {column} ...)

Before > "In sheet '{sheetName}', row {rownum}, column {column}: Name is required"

After  > "In sheet 'Sheet1', row 1, column 1: Name is required"

Alternatives

Regarding compatibility support for the javax.validation namespace, an alternative approach was considered: maintaining only a single source module, fesod-bval (for Jakarta), and then leveraging a build plugin like maven-shade-plugin, transformer-maven-plugin to dynamically transform the jakarta.* namespace into javax.* during the packaging phase.

The intended outcome of this strategy was to generate two distinct artifacts using a classifier, allowing users to choose the one that fits their environment:

<!-- For Jakarta support -->
<dependency>
    <groupId>org.apache.fesod</groupId>
    <artifactId>fesod-bval</artifactId>
    <version>${fesod.version}</version>
</dependency>

<!-- For Javax support -->
<dependency>
    <groupId>org.apache.fesod</groupId>
    <artifactId>fesod-bval</artifactId>
    <version>${fesod.version}</version>
    <classifier>javax</classifier>
</dependency>

Problem:

The build plugins are unable to modify the dependency declarations within the generated pom.xml file. This would require manual intervention after the build, which is incompatible with and unfriendly to our automated CI/CD processes, such as the GitHub workflows.

Anything else?

No response

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions