ExcelNinja is a lightweight, modern Java library that makes Excel file handling as swift and precise as a ninja's blade.
Built on top of Apache POI, it provides a clean, annotation-driven API for seamless conversion between Java Object and Excel documents.
- Simple & Intuitive: One-line reads and writes with automatic type conversion
- Annotation-Driven: Map Excel columns to DTO fields using
@ExcelReadColumnand@ExcelWriteColumn - Bidirectional: Read Excel β DTO lists, Write DTO lists β Excel
- οΈType-Safe: Built-in validation with domain-driven value objects
- οΈModern Architecture: Clean hexagonal architecture with proper separation of concerns
- Performance Logging: Built-in performance metrics and detailed logging
- Java 8+ Compatible: Works with Java 8 and above
dependencies {
implementation 'io.github.excel-ninja:excelNinja:1.0.2'
}Zero Configuration Required
ExcelNinja bundles all dependencies internally - no need to add Apache POI manually.
public class User {
@ExcelReadColumn(headerName = "ID")
@ExcelWriteColumn(headerName = "ID", order = 0)
private Long id;
@ExcelReadColumn(headerName = "Name")
@ExcelWriteColumn(headerName = "Name", order = 1)
private String name;
@ExcelReadColumn(headerName = "Age")
@ExcelWriteColumn(headerName = "Age", order = 2)
private Integer age;
@ExcelReadColumn(headerName = "Email")
@ExcelWriteColumn(headerName = "Email", order = 3)
private String email;
// Constructors, getters, and setters...
}// From file path - reads first sheet
List<User> users = NinjaExcel.read("users.xlsx", User.class);
// From File object - reads first sheet
File file = new File("users.xlsx");
List<User> users = NinjaExcel.read(file, User.class);
// Read specific sheet
List<User> users = NinjaExcel.readSheet("users.xlsx", "Sheet1", User.class);
// Read all sheets
Map<String, List<User>> allSheets = NinjaExcel.readAllSheets("users.xlsx", User.class);
// Get sheet names
List<String> sheetNames = NinjaExcel.getSheetNames("users.xlsx");
// read(), readSheet(), readSheets(), getSheetNames() only parse the sheets they need.List<User> users = Arrays.asList(
new User(1L, "Alice", 28, "alice@example.com"),
new User(2L, "Bob", 32, "bob@example.com")
);
// Create workbook with single sheet
ExcelWorkbook workbook = ExcelWorkbook.builder()
.sheet("Users", users)
.build();
// Write to file
NinjaExcel.write(workbook, "output.xlsx");
// Or write to OutputStream
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
NinjaExcel.write(workbook, out);
}
// Multiple sheets
List<Employee> employees = Arrays.asList(/* employee data */);
ExcelWorkbook multiSheet = ExcelWorkbook.builder()
.sheet("Users", users)
.sheet("Employees", employees)
.build();
NinjaExcel.write(multiSheet, "multi-sheet.xlsx");ExcelNinja includes built-in support for common types including LocalDate, numeric strings, and blank-cell defaults:
public class Employee {
@ExcelReadColumn(headerName = "Hire Date")
private LocalDate hireDate;
@ExcelReadColumn(headerName = "Salary", defaultValue = "0")
private Double salary;
// Blank cells use defaultValue when provided.
// Numeric strings like "123.45" also convert to numeric fields.
// Supports various date formats automatically:
// yyyy-MM-dd, dd/MM/yyyy, MM/dd/yyyy, etc.
}ExcelNinja automatically switches to streaming mode for large files (>10MB) to optimize memory usage:
// For very large files, read in chunks
try (ChunkReader<User> chunks = NinjaExcel.readInChunks("large-file.xlsx", User.class, 1000)) {
while (chunks.hasNext()) {
List<User> chunk = chunks.next();
// Process chunk
}
}
// Full iteration closes internal file resources automatically.
// try-with-resources also closes safely when you stop early.
// Read multiple specific sheets
List<String> sheetsToRead = Arrays.asList("Users", "Customers");
Map<String, List<User>> selectedSheets = NinjaExcel.readSheets("workbook.xlsx", User.class, sheetsToRead);For advanced workbook creation:
// Using builder pattern for complex workbooks
ExcelWorkbook workbook = ExcelWorkbook.builder()
.sheet("Users", userList)
.sheet("Products", productList)
.metadata(new WorkbookMetadata()) // Optional metadata, persisted as workbook core properties
.build();
// Auto-size is opt-in for write performance on large exports
ExcelSheet sizedSheet = ExcelSheet.builder()
.name("Users")
.headers("Name")
.rows(Collections.singletonList(Collections.singletonList("Alice Johnson")))
.autoSizeColumns()
.build();
// Access workbook information
Set<String> sheetNames = workbook.getSheetNames();
ExcelSheet userSheet = workbook.getSheet("Users");
WorkbookMetadata metadata = workbook.getMetadata();ExcelNinja follows clean architecture principles:
π Application Layer
βββ facade/
β βββ NinjaExcel.java # Main API facade
βββ port/
βββ ConverterPort.java # Type conversion interface
π Domain Layer
βββ model/ # Value objects & domain models
β βββ ExcelWorkbook.java # Core workbook aggregate
β βββ ExcelSheet.java # Sheet representation
β βββ Headers.java # Header management
β βββ DocumentRows.java # Row collection
β βββ SheetName.java # Sheet name validation
βββ annotation/ # Column mapping annotations
βββ port/ # Domain ports
β βββ WorkbookReader.java # Reading interface
β βββ WorkbookWriter.java # Writing interface
βββ exception/ # Domain-specific exceptions
π Infrastructure Layer
βββ io/ # Apache POI & streaming adapters
β βββ PoiWorkbookReader.java # Standard POI reader
β βββ PoiWorkbookWriter.java # Standard POI writer
β βββ StreamingWorkbookReader.java # Streaming reader for large files
βββ converter/ # Type conversion implementation
βββ metadata/ # Entity metadata handling
βββ util/ # Reflection utilities
Maps DTO fields to Excel columns when reading:
@ExcelReadColumn(
headerName = "Full Name", // Required: Excel column header
type = String.class, // Optional: Override field type
defaultValue = "Unknown" // Optional: Default if cell is empty
)
private String name;Maps DTO fields to Excel columns when writing:
@ExcelWriteColumn(
headerName = "Employee ID", // Required: Excel column header
order = 0 // Optional: Column order (default: field order)
)
private Long id;| Component | Description |
|---|---|
NinjaExcel |
Main facade providing read() and write() methods |
ExcelWorkbook |
Workbook representation supporting multiple sheets |
@ExcelReadColumn |
Annotation for Excel β DTO mapping |
@ExcelWriteColumn |
Annotation for DTO β Excel mapping |
DefaultConverter |
Handles type conversion between Excel and Java types |
ExcelNinja provides detailed performance metrics:
[NINJA-EXCEL] Reading Excel file: users.xlsx (245.2 KB)
[NINJA-EXCEL] Successfully read 1000 records from users.xlsx in 150 ms (6666.67 records/sec)
[NINJA-EXCEL] Writing Excel document with 500 records to output.xlsx
[NINJA-EXCEL] Successfully wrote 500 records to output.xlsx (89.4 KB) in 95 ms (5263.16 records/sec)
ExcelNinja provides comprehensive error handling with descriptive messages:
try {
List<User> users = NinjaExcel.read("missing.xlsx", User.class);
} catch (DocumentConversionException e) {
// Detailed error messages for:
// - File not found
// - Invalid Excel format
// - Type conversion failures
// - Missing headers
// - Annotation configuration errors
}ExcelNinja includes comprehensive test coverage:
- β Unit tests for all core components
- β Integration tests for end-to-end workflows
- β Value object validation tests
- β Exception handling tests
- β Performance benchmarks
Run tests with: ./gradlew test
We welcome contributions! ExcelNinja is designed to be:
- Extensible: Easy to add new type converters
- Maintainable: Clean architecture with proper separation
- Testable: Comprehensive test coverage
- Documentation-first: Clear APIs and examples
git clone https://github.com/excel-ninja/excel-ninja-toolkit.git
cd excel-ninja-toolkit
./gradlew build- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Add tests for your changes
- Update documentation if needed
- Ensure all tests pass (
./gradlew test) - Submit a pull request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
ExcelNinja is designed for high performance:
- Automatic Optimization: Files >10MB automatically use streaming (SAX-based) for memory efficiency
- Metadata Caching: Reflection metadata is cached in a thread-safe LRU cache (max 1000 entries)
- Chunk Processing: Process large files in configurable chunks (default: 1000 rows)
- Built-in Metrics: Automatic performance logging (records/sec, file size, duration)
Benchmarks (approximate, depends on hardware):
- Small files (<10MB): ~5,000-10,000 records/sec (POI mode)
- Large files (>10MB): ~3,000-8,000 records/sec (Streaming mode)
- Memory usage: Streaming mode uses ~50-100MB regardless of file size
- High-precision
BigDecimalvalues that cannot be represented exactly as Excel numeric cells are written as text to preserve the exact value.
If you find ExcelNinja helpful, please consider:
- Starring this repository
- Reporting issues on GitHub Issues
- Suggesting features
- Contributing code
For bug reports, please include:
- ExcelNinja version
- Java version
- Sample code or Excel file (if possible)
- Error messages and stack traces
Made by the ExcelNinja team
"Making Excel handling in Java as swift and precise as a ninja's blade"
