[bug]Custom converters with supportExcelTypeKey() == null are silently ignored when writing CSV
- 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
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