If you've ever processed an Excel file in Java, you know the drill.
Import Apache POI. Write 60 lines of boilerplate just to read a column header. Spend the next hour debugging a NullPointerException on row 14 because someone left a cell blank. Cry a little.
Sound familiar?
Meet Sheetz — an open-source Java library that collapses all of that pain into a single line of code:
List<Product> products = Sheetz.read("products.xlsx", Product.class);
That's it. No boilerplate. No ceremony. Just data.
🤔 Why Another Excel Library?
Apache POI is powerful — but it was never designed for developer ergonomics. EasyExcel, FastExcel, and Poiji are better, but they all require significant setup before you can do anything useful.
Sheetz takes a different philosophy: zero config by default, full control when you need it.
Here's what you get out of the box:
- 🚀 One-liner API —
Sheetz.read(),Sheetz.write(), done - 📊 True SAX Streaming — process million-row files with constant ~10MB memory
- 🔄 Auto Type Conversion — 19 built-in converters: dates, enums, BigDecimal, UUID, and more
- 🧵 Thread-Safe — designed for concurrent use in multi-threaded apps
- ✅ Built-in Validation — detailed error reporting with row/column context
- 📝 Annotation Mapping —
@Columnfor custom headers, required fields, and defaults
⚡ Getting Started in 2 Minutes
Add the Dependency
Maven:
<dependency>
<groupId>io.github.chitralabs.sheetz</groupId>
<artifactId>sheetz-core</artifactId>
<version>1.0.1</version>
</dependency>
Gradle:
implementation 'io.github.chitralabs.sheetz:sheetz-core:1.0.1'
Define Your Model
public class Product {
public String name;
public Double price;
public Boolean inStock;
public LocalDate releaseDate;
public Product() {} // Required no-arg constructor
}
Read, Write, Done
// Read Excel
List<Product> products = Sheetz.read("products.xlsx", Product.class);
// Read CSV
List<Product> products = Sheetz.read("products.csv", Product.class);
// No model? No problem — 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 Large Files (The Killer Feature)
This is where Sheetz really shines. Processing a file with 1 million rows? Use stream() and memory stays flat at ~10MB 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 100K rows, always use
stream(). Loading everything into memory withread()on a million-row file is a fast track toOutOfMemoryError.
🏷️ Annotation-Based Mapping with @Column
Headers in your spreadsheet don't match your field names? Columns 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;
}
✅ Built-in Data Validation
Stop writing manual row-parsing error handlers. Sheetz gives you structured validation results with precise row/column context:
ValidationResult<Product> 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());
}
// Get only the clean rows
List<Product> validProducts = result.validRows();
🏗️ Builder API for Full Control
Need to select a specific sheet? Set a custom header row? Auto-size columns? The fluent builder has you covered:
// Reader Builder
List<Product> 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 — this one's a gem 💎
Sheetz.workbook()
.sheet("Products", products)
.sheet("Employees", employees)
.sheet("Orders", orders)
.write("report.xlsx");
🔧 Custom Converters
Got a money column prefixed with $? A legacy date format? Write a converter once and reuse it everywhere:
public class MoneyConverter implements Converter<BigDecimal> {
@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 (19 Built-In Converters)
No more manual Integer.parseInt() or LocalDate.parse() calls. Sheetz auto-converts all of these:
| 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 at a Glance
| 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? One config object to rule them all:
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);
🆚 How Does It Compare?
The sheetz-benchmarks repo has side-by-side JMH performance comparisons against Apache POI, EasyExcel, FastExcel, and Poiji—with full source code so you can run them yourself.
And if you want to see every feature in action, the sheetz-examples repo has 8 runnable demos covering end-to-end use cases.
🚀 Try It Now
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
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
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!
Top comments (0)