apache / apache/fesod

[bug]Custom converters with supportExcelTypeKey() == null are silently ignored when writing CSV

Open Beginner friendly
#1,056 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
6.2k
Forks
532
Avg merge
1d 3h
Merged PRs (30d)
42

Description

### Search before asking

- [x] I searched in the issues and found nothing similar.
(EasyExcel#3054 reported the same root cause, but no corresponding issue exists in this repo after the Fesod migration.)

### Fesod version

2.0.1-incubating (the code path is unchanged on current `main`)

### JDK version

1.8.0_482

### Operating system

macOS 15.2

### Steps To Reproduce

Register a custom converter whose `supportExcelTypeKey()` returns `null` (the documented way to match all Excel cell types), then write the same data as xlsx and as CSV:
```java
static class BooleanYesNoConverter implements Converter {
@Override
public Class supportJavaTypeKey() {
return Boolean.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
// null = match all Excel cell data types
return null;
}

@Override
public WriteCellData convertToExcelData(WriteConverterContext context) {
return new WriteCellData<>(Boolean.TRUE.equals(context.getValue()) ? "YES" : "NO");
}
}
```
// 1) xlsx: the custom converter works
FesodSheet.write(xlsxFile)
.excelType(ExcelTypeEnum.XLSX)
.registerConverter(new BooleanYesNoConverter())
.sheet()
.doWrite(Collections.singletonList(Collections.singletonList(Boolean.TRUE)));

// 2) csv: the same converter is silently ignored
FesodSheet.write(csvFile)
.excelType(ExcelTypeEnum.CSV)
.charset(StandardCharsets.UTF_8)
.registerConverter(new BooleanYesNoConverter())
.sheet()
.doWrite(Collections.singletonList(Collections.singletonList(Boolean.TRUE)));

### Current Behavior

- xlsx output: `YES` (custom converter applied)
- CSV output: `true` — the custom converter is **silently ignored**, the built-in `BooleanStringConverter` takes over. No warning is logged.

### Expected Behavior

The custom converter should apply for CSV as well: output `YES`.

### Root cause

`AbstractExcelWriteExecutor#doConvert` forces the lookup key to `(JavaType, STRING)` in CSV mode:
```java
if (converter == null) { // csv is converted to string by default
if (writeContext.writeWorkbookHolder().getExcelType() == ExcelTypeEnum.CSV) {
cellWriteHandlerContext.setTargetCellDataType(CellDataTypeEnum.STRING);
}
converter = writeContext.currentWriteHolder().converterMap()
.get(ConverterKeyBuild.buildKey(originalFieldClass, targetCellDataType));
}
```

- xlsx: lookup key is `(Boolean, null)` → hits the custom converter registered under `(Boolean, null)` ✓
- csv: lookup key is forced to `(Boolean, STRING)` → the custom converter misses, the built-in `BooleanStringConverter` wins ✗

Note the trap is two-sided: changing `supportExcelTypeKey()` to `STRING` fixes CSV but **silently breaks xlsx** (the lookup then misses for xlsx and the built-in `BooleanBooleanConverter` writes a BOOLEAN cell). A single static registration key cannot match both formats.

EasyExcel#3054 reported the same root cause ("custom converter is not applied when writing CSV"). Since Fesod inherits this code path, the problem carries over.

### Suggested fixes (any one would work)

1. In CSV mode, fall back to the `(JavaType, null)` lookup when `(JavaType, STRING)` misses (best backward compatibility);
2. Or register null-key converters under both `(JavaType, null)` and `(JavaType, STRING)` at registration time;
3. Or at minimum log a warning when a registered custom converter is shadowed in CSV mode.

### Workaround

For anyone hitting this: wrap the converter for CSV writes only and force its registration key to `STRING`, delegating the conversion logic:
```java
static class CsvStringKeyAdapter implements Converter {
private final Converter delegate;
CsvStringKeyAdapter(Converter delegate) {
this.delegate = delegate;
}
@Override
public Class supportJavaTypeKey() { return delegate.supportJavaTypeKey(); }

@Override
public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; }

@Override public WriteCellData convertToExcelData(WriteConverterContext context) throws Exception {
return delegate.convertToExcelData(context);
}
}
// CSV write: builder.registerConverter(new CsvStringKeyAdapter<>(myConverter));
// xlsx write: builder.registerConverter(myConverter);
```
### Are you willing to submit a PR?

- [x] I'm willing to submit a PR!

Contributor guide

Open the contributing guide

Research direction

Start at AbstractExcelWriteExecutor#doConvert and trace converter lookup for XLSX and CSV, then reproduce the two cases from the issue. Add coverage for a null-key custom converter in both formats and verify that CSV produces YES while XLSX behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
Half a day
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.