Stop Wrestling With Apache POI—Meet Sheetz, the One-Liner Excel Library for Java
Source: Dev.to
If you’ve ever processed an Excel file in Java, you know the drill:
- Import Apache POI.
- Write dozens of lines of boiler‑plate just to read a column header.
- Spend the next hour debugging a
NullPointerExceptionon row 14 because someone left a cell blank.
Sound familiar?
Sheetz is an open‑source Java library that collapses all of that pain into a single line of code:
List products = Sheetz.read("products.xlsx", Product.class);
No boiler‑plate. No ceremony. Just data.
Why Sheetz?
Apache POI is powerful, but it wasn’t designed for developer ergonomics.
EasyExcel, FastExcel, and Poiji are better, yet they still require a fair amount of setup.
Sheetz takes a different philosophy: zero config by default, full control when you need it.
Out‑of‑the‑box features
| ✅ | Feature |
|---|---|
| 🚀 | One‑liner API – Sheetz.read(), Sheetz.write(), done |
| 📊 | True SAX streaming – process million‑row files with constant ~10 MB memory |
| 🔄 | Auto type conversion – 19 built‑in converters (dates, enums, BigDecimal, UUID, …) |
| 🧵 | Thread‑safe – safe for concurrent use |
| ✅ | Built‑in validation – detailed error reporting with row/column context |
| 📝 | Annotation mapping – @Column for custom headers, required fields, defaults, etc. |
Dependency
Maven
io.github.chitralabs.sheetz
sheetz-core
1.0.1
Gradle
implementation 'io.github.chitralabs.sheetz:sheetz-core:1.0.1'
Simple POJO
public class Product {
public String name;
public Double price;
public Boolean inStock;
public LocalDate releaseDate;
public Product() {} // Required no‑arg constructor
}
Reading & Writing
// Read Excel
List products = Sheetz.read("products.xlsx", Product.class);
// Read CSV
List productsCsv = Sheetz.read("products.csv", Product.class);
// No model? Read as maps
List<Map<String, Object>> data = Sheetz.readMaps("products.xlsx");
// Write Excel
Sheetz.write(products, "output.xlsx");
// Write CSV
Sheetz.write(products, "output.csv");
Streaming – Constant Memory
Processing a file with 1 million rows? Use stream() and memory stays flat at ~10 MB regardless of file size.
// Process row‑by‑row – constant memory
Sheetz.stream("huge-file.xlsx", Product.class)
.forEach(product -> process(product));
// Process in batches – perfect for bulk DB inserts
Sheetz.stream("huge-file.xlsx", Product.class)
.batch(1000)
.forEach(batch -> database.bulkInsert(batch));
// Full Java Streams support
long count = Sheetz.stream("huge-file.xlsx", Product.class)
.stream()
.filter(p -> p.price > 100)
.count();
⚠️ Rule of thumb: For files over 100 K rows, always use
stream(). Loading everything withread()on a million‑row file will quickly trigger anOutOfMemoryError.
@Column – Flexible Mapping
Headers don’t match field names? Columns are in a weird order? Need defaults for empty cells? @Column handles it all.
public class Product {
@Column("Product Name") // Map to a different header
public String name;
@Column(index = 1) // Map by column index (0‑based)
public Double price;
@Column(required = true) // Fail validation if empty
public String sku;
@Column(defaultValue = "pending") // Default for empty cells
public String status;
@Column(format = "dd/MM/yyyy") // Custom date format
public LocalDate orderDate;
@Column(converter = MoneyConverter.class) // Custom converter
public BigDecimal amount;
@Column(ignore = true) // Skip this field entirely
public String internalId;
@Column(width = 20) // Column width in chars (write‑only)
public String description;
}
Validation with Rich Error Details
ValidationResult result = Sheetz.validate("products.csv", Product.class);
System.out.println("Valid rows: " + result.validCount());
System.out.println("Errors: " + result.errorCount());
System.out.println("Success rate: " + result.successRate() + "%");
for (ValidationResult.RowError error : result.errors()) {
System.out.println("Row " + error.row() +
", Col '" + error.column() +
"': " + error.message());
}
// Clean rows only
List validProducts = result.validRows();
Fluent Builder – Full Control
// Reader Builder
List products = Sheetz.reader(Product.class)
.file("products.xlsx")
.sheet("Inventory") // By name
.headerRow(1) // Header on row 2 (0‑based)
.delimiter(';') // For semicolon‑delimited CSVs
.read();
// Writer Builder
Sheetz.writer(Product.class)
.data(products)
.file("output.xlsx")
.sheet("Products")
.autoSize(true) // Auto‑fit column widths
.freezeHeader(true) // Freeze the header row
.write();
Multi‑Sheet Workbook
Sheetz.workbook()
.sheet("Products", products)
.sheet("Employees", employees)
.sheet("Orders", orders)
.write("report.xlsx");
Custom Converter Example
public class MoneyConverter implements Converter {
@Override
public BigDecimal fromCell(Object value, ConvertContext ctx) {
String str = value.toString()
.replace("$", "")
.replace(",", "")
.trim();
return new BigDecimal(str);
}
@Override
public Object toCell(BigDecimal value) {
return "$" + value.setScale(2, RoundingMode.HALF_UP);
}
}
Use per‑field via annotation
@Column(converter = MoneyConverter.class)
public BigDecimal price;
Or register globally for all BigDecimal fields
Sheetz.register(BigDecimal.class, new MoneyConverter());
Supported Types & Example Values
| Type | Example Values |
|---|---|
| String | Any text |
| Integer, Long, Double, Float | 42, 3.14 |
| BigDecimal, BigInteger | 99.99, 999999999 |
| Boolean | true, yes, y, 1, on (case‑insensitive) |
| LocalDate, LocalDateTime | 2024-01-15, 2024-01-15 10:30:00 |
| ZonedDateTime, Instant | 2024-01-15T10:30:00Z |
| UUID | 550e8400-e29b-41d4-a716-446655440000 |
| Enum | ACTIVE, active (case‑insensitive) |
Format Support
| Format | Read | Write | Streaming |
|---|---|---|---|
.xlsx (Excel 2007+) | ✅ | ✅ | ✅ SAX / SXSSF |
.xls (Excel 97‑2003) | ✅ | ✅ | ❌ |
.csv | ✅ | ✅ | ✅ Buffered |
Global Configuration
Want to change the default date format or CSV encoding across your whole app? Create a single configuration object and apply it globally:
SheetzConfig config = SheetzConfig.builder()
.dateFormat("dd/MM/yyyy")
.dateTimeFormat("dd/MM/yyyy HH:mm")
.trimValues(true)
.skipEmptyRows(true)
.streamingThreshold(10_000) // Auto‑stream above this row count
.charset(StandardCharsets.ISO_8859_1)
.build();
Sheetz.configure(config);
Getting Started
git clone https://github.com/chitralabs/sheetz.git
cd sheetz
mvn clean install
Requirements
- Java 11+
- Apache POI 5.2.5
- OpenCSV 5.9
License: Apache 2.0
Benchmarks & Examples
- Benchmarks – The
sheetz-benchmarksrepository contains side‑by‑side JMH performance comparisons against Apache POI, EasyExcel, FastExcel, and Poiji, with full source code so you can run them yourself. - Examples – The
sheetz-examplesrepository provides 8 runnable demos covering end‑to‑end use cases.
Contribute
Sheetz is fresh out of the oven and actively looking for contributors, issues, and feedback. If you’ve ever wanted Excel/CSV processing that just works without a week of POI setup, give it a spin and drop a ⭐ if you find it useful.
👉 GitHub: https://github.com/chitralabs/sheetz
Discussion
What’s your current go‑to library for Excel or CSV processing in Java? Drop it in the comments—I’m curious how the community handles this pain point!