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.5</version>
</dependency>

Gradle:

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

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, 10-column schema, shared string table)

Read:

Library Time Memory
jackson-spreadsheet 287 ms 579 MB
FastExcel 265 ms 722 MB
Apache Fesod 537 ms 882 MB
Apache POI 1998 ms 4287 MB

Write:

Library Time Memory
jackson-spreadsheet 327 ms 271 MB
FastExcel 353 ms 329 MB
Apache POI (SXSSF) 664 ms 520 MB
Apache Fesod 791 ms 1096 MB

See BENCHMARK.md for the full per-scale tables (1K – 500K), sustained 60-second throughput, and shared-strings strategies.

Feature Comparison

Feature jackson-spreadsheet Apache POI Apache 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 Apache Fesod? Apache 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? Throughput close to FastExcel at 100K rows (writer slightly faster), with ~20% less allocation in both read and write. ~7x faster than Apache POI on read. 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. Available 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