Skip to content

scndry/jackson-dataformat-spreadsheet

Repository files navigation

jackson-dataformat-spreadsheet

Build Maven Central License

A Java library (Jackson extension) for reading and writing Excel spreadsheets (XLSX/XLS) as POJOs.

Map spreadsheet rows to Java objects with ObjectMapper — the same API you use for JSON, CSV, and XML. No cell-level iteration, no column index counting, no manual type casting.

What It Does

SpreadsheetMapper mapper = new SpreadsheetMapper();

// Read Excel to POJOs
List<Employee> employees = mapper.readValues(file, Employee.class);

// Write POJOs to Excel
mapper.writeValue(file, employees, Employee.class);

That's it. If you know Jackson, you know this library.

When To Use This

  • You need to read XLSX/XLS files into Java objects without writing cell-by-cell parsing code
  • You need to write Java objects to Excel with headers, types, and styling
  • You want Jackson ecosystem integration (custom deserializers, mix-ins, modules)
  • You have nested object structures that need to flatten into spreadsheet columns
  • You need streaming performance for large files (100K+ rows)

Installation

Available on Maven Central:

Maven:

<dependency>
    <groupId>io.github.scndry</groupId>
    <artifactId>jackson-dataformat-spreadsheet</artifactId>
    <version>1.6.4</version>
</dependency>

Gradle:

implementation "io.github.scndry:jackson-dataformat-spreadsheet:1.6.4"

Requirements

  • Java 8+
  • Jackson 2.14.0+
  • Apache POI 4.1.1+ (Strict OOXML requires 5.1.0+)

Quick Start

Define Your Model

@DataGrid
public class Product {
    private String name;
    private int quantity;
    private double price;
    // constructors, getters, setters
}

Read from Excel

SpreadsheetMapper mapper = new SpreadsheetMapper();

// Single object (first row)
Product product = mapper.readValue(file, Product.class);

// All rows
List<Product> products = mapper.readValues(file, Product.class);

// Specific sheet
SheetInput<File> input = SheetInput.source(file, "Products");
List<Product> products = mapper.readValues(input, Product.class);

Write to Excel

List<Product> products = Arrays.asList(
    new Product("Apple", 10, 1.50),
    new Product("Banana", 20, 0.80));

// Single object
mapper.writeValue(file, products.get(0), Product.class);

// All rows
mapper.writeValue(file, products, Product.class);

// Specific sheet
SheetOutput<File> output = SheetOutput.target(file, "Products");
mapper.writeValue(output, products, Product.class);

Complex Objects

Nested objects flatten into columns. Lists of nested objects expand into multiple rows.

@DataGrid(mergeColumn = OptBoolean.TRUE)
class Order {
    @DataColumn("ID")         int id;
    @DataColumn("Customer")   String customer;
    @DataColumnGroup("Items") List<LineItem> items;
    @DataColumn("Total")      BigDecimal total;
}

class LineItem {
    @DataColumn("SKU")    String sku;
    @DataColumn("Qty")    int qty;
    @DataColumn("Amount") BigDecimal amount;
}
+----+----------+----------------------+-------+
| ID | Customer |        Items         | Total |
|    |          +------+-----+---------+       |
|    |          | SKU  | Qty | Amount  |       |
+----+----------+------+-----+---------+-------+
|    |          | A-01 |  3  | 30.00   |       |
|  1 | Alice    +------+-----+---------+ 95.00 |
|    |          | B-02 |  5  | 65.00   |       |
+----+----------+------+-----+---------+-------+

How It Compares

Performance (100K rows, mixed types, shared string table)

Read:

Library Time Memory
jackson-spreadsheet 190 ms 360 MB
FastExcel 208 ms 407 MB
Fesod 266 ms 381 MB
Poiji 809 ms 2739 MB
Apache POI 1173 ms 2227 MB

Write:

Library Time Memory
jackson-spreadsheet 138 ms 125 MB
FastExcel 152 ms 149 MB
Apache POI 269 ms 204 MB
Fesod 323 ms 458 MB

Fastest read and write throughput, lowest write memory at 100K rows. See BENCHMARK.md for full results.

Feature Comparison

Feature jackson-spreadsheet Apache POI Fesod FastExcel
POJO data binding Yes No Yes No
Nested object support Yes No No No
Jackson ecosystem Yes No No No
Streaming read Yes¹ Event API Yes Yes
Streaming write Yes¹ SXSSF Yes Yes
Cell styling Yes Yes Yes No
XLSX read/write Yes Yes Yes Yes
XLS read/write Yes Yes No No
Annotation mapping Yes No Yes No

¹ XLSX read streams via StAX, write via StringBuilder over a POI scaffold; XLS uses in-memory POI workbook (HSSF has no streaming API).

Key Features

Streaming for Large Files

SpreadsheetReader reader = mapper.sheetReaderFor(Product.class);
try (SheetMappingIterator<Product> iter = reader.readValues(input)) {
    while (iter.hasNext()) {
        Product p = iter.next();
        SheetLocation loc = iter.getCurrentLocation();
        // loc.getRow(), loc.getColumn() — zero-based cell position
    }
}

Cell Styling

StylesBuilder.simple() for a per-type starter set; build a StylesBuilder from scratch for full control.

StylesBuilder styles = new StylesBuilder()
    .cellStyle("currency")
        .dataFormat("#,##0.00")
        .font().bold().end()
        .end();

SpreadsheetMapper mapper = SpreadsheetMapper.builder()
    .stylesBuilder(styles)
    .build();

Sheet-Level Features

GridConfigurer adds conditional formatting, freeze pane, and auto filter on top of the data grid — without dropping into POI Sheet/Cell code.

SpreadsheetMapper mapper = SpreadsheetMapper.builder()
    .stylesBuilder(styles)
    .gridConfigurer(new GridConfigurer()
        .freezePane(0, 1)
        .autoFilter()
        .conditionalFormatting("score",
            greaterThanOrEqual(80).style("highlight")))
    .build();

Conditional formatting rules reference columns from the model class and styles from StylesBuilder — both name-based and resolved at write time. Static-import the factory methods from ConditionalFormats for fluent chaining; see the GUIDE for typed operators, multi-rule columns, and color scale.

Configuration

SpreadsheetMapper mapper = SpreadsheetMapper.builder()
    .origin("B2")           // start at B2 instead of A1
    .useHeader(false)        // no header row
    .enable(SheetParser.Feature.BREAK_ON_BLANK_ROW)
    .build();

Excel Date Handling

Built-in conversion between Java date types and Excel serial numbers. Registered by default — no setup needed. Excel renders a date cell as a date only when it carries a date format; StylesBuilder.simple() registers per-type defaults as a starter.

Supported: Date, Calendar, LocalDate, LocalDateTime

Architecture

Not a POI wrapper. Extends Jackson's streaming layer directly:

  • SheetParser extends ParserMinimalBase — StAX pull parser
  • SheetGenerator extends GeneratorBase — streaming cell writer
  • SpreadsheetFactory extends JsonFactory — format detection

The XLSX path bypasses POI's User Model by default — the read path parses OOXML XML directly via StAX, the write path streams XML via StringBuilder with a POI scaffold for package metadata.

See ARCHITECTURE.md for design decisions and data flow diagrams.

Documentation

FAQ

Q: How is this different from Apache POI? POI gives you cells. This gives you POJOs. You define a class with @DataGrid, and mapper.readValues() returns typed objects. No row.getCell(0).getStringCellValue().

Q: How is this different from Fesod? Fesod has its own API. This extends Jackson's ObjectMapper, so you get the full Jackson ecosystem — custom deserializers, mix-ins, modules, polymorphic types.

Q: Does it support nested objects? Yes. Nested POJOs automatically flatten to columns on write and reconstruct on read. No configuration needed.

Q: How does performance compare? Fastest read and write throughput at 100K rows, with the lowest write memory. 6x faster read than Apache POI. Default writer is 10% faster than FastExcel and uses 16% less memory. See BENCHMARK.md.

Q: What Excel formats are supported? XLSX (OOXML) and XLS (legacy). XLSX uses StAX streaming; XLS uses POI object model.

Q: Is it production-ready? Yes. Version 1.6.4 on Maven Central. Java 8+, Jackson 2.14+, POI 4.1.1+. Listed as a community data format module in the FasterXML jackson repository.

Q: Is the mapper thread-safe? The mapper instance is reusable across threads once configured (same rule as Jackson's ObjectMapper). Concurrent calls with File / InputStream / OutputStream inputs are safe — the library opens an isolated Workbook per call. If you pass a Sheet directly, POI's Workbook/Sheet are not thread-safe, so each thread needs its own.

License

Apache License 2.0

About

A Java library (Jackson extension) for reading and writing Excel spreadsheets (XLSX/XLS) as POJOs.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages