Stop Wrestling With Apache POI—Meet Sheetz, the One-Liner Excel Library for Java

Published: (February 18, 2026 at 12:18 AM EST)
6 min read
Source: Dev.to

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 NullPointerException on 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 with read() on a million‑row file will quickly trigger an OutOfMemoryError.

@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

TypeExample Values
StringAny text
Integer, Long, Double, Float42, 3.14
BigDecimal, BigInteger99.99, 999999999
Booleantrue, yes, y, 1, on (case‑insensitive)
LocalDate, LocalDateTime2024-01-15, 2024-01-15 10:30:00
ZonedDateTime, Instant2024-01-15T10:30:00Z
UUID550e8400-e29b-41d4-a716-446655440000
EnumACTIVE, active (case‑insensitive)

Format Support

FormatReadWriteStreaming
.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-benchmarks repository 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-examples repository 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!

0 views
Back to Blog

Related posts

Read more »