Skip to main content

Cell Merging

This chapter introduces cell merging strategies when writing Excel files.

Overview

When generating Excel reports, you may need to merge cells — for example, merging repeated category names in a column, or creating summary rows. Fesod supports two approaches: annotation-based and strategy-based merging.

Annotation-Based Merging

Overview

Annotate a field with @ContentLoopMerge(eachRow = N) to automatically merge every N rows in that column.

POJO Class

@Getter
@Setter
@EqualsAndHashCode
public class DemoMergeData {
@ContentLoopMerge(eachRow = 2)
@ExcelProperty("String Title")
private String string;

@ExcelProperty("Date Title")
private Date date;

@ExcelProperty("Number Title")
private Double doubleData;
}

Code Example

@Test
public void annotationMergeWrite() {
String fileName = "annotationMergeWrite" + System.currentTimeMillis() + ".xlsx";
FesodSheet.write(fileName, DemoMergeData.class)
.sheet()
.doWrite(data());
}

Result

| String Title | Date Title          | Number Title |
|--------------|---------------------|--------------|
| String0 | 2025-01-01 00:00:00 | 0.56 |
| (merged) | 2025-01-01 00:00:00 | 0.56 |
| String1 | 2025-01-01 00:00:00 | 0.56 |
| (merged) | 2025-01-01 00:00:00 | 0.56 |

Strategy-Based Merging

Overview

Register a LoopMergeStrategy(eachRow, columnIndex) as a write handler. More flexible — can be configured at runtime and applied to any column.

Code Example

@Test
public void strategyMergeWrite() {
String fileName = "strategyMergeWrite" + System.currentTimeMillis() + ".xlsx";

// Merge every 2 rows in column 0
LoopMergeStrategy strategy = new LoopMergeStrategy(2, 0);
FesodSheet.write(fileName, DemoData.class)
.registerWriteHandler(strategy)
.sheet()
.doWrite(data());
}

Custom Merge Strategy

Overview

For complex merging logic (e.g., merge based on data content), implement the AbstractMergeStrategy class.

Code Example

public class CustomMergeStrategy extends AbstractMergeStrategy {
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (relativeRowIndex != null && relativeRowIndex % 2 == 0 && head.getColumnIndex() == 0) {
// Note: +1 assumes a single header row. For multi-row headers, use cell.getRowIndex() instead.
int startRow = relativeRowIndex + 1;
int endRow = startRow + 1;
sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, 0, 0));
}
}
}

Usage:

@Test
public void customMergeWrite() {
String fileName = "customMergeWrite" + System.currentTimeMillis() + ".xlsx";
FesodSheet.write(fileName, DemoData.class)
.registerWriteHandler(new CustomMergeStrategy())
.sheet()
.doWrite(data());
}

When to Use Which

ApproachBest For
@ContentLoopMergeSimple, fixed merge patterns baked into the data class
LoopMergeStrategyDynamic merging, runtime configuration
AbstractMergeStrategyComplex merging based on data content or custom rules