Skip to content

fix: honor wildcard-key custom converters when writing CSV (#1045) (#1056) - #1069

Open
Mikkey-f wants to merge 1 commit into
apache:mainfrom
Mikkey-f:feat/1045-csv-wildcard-converter
Open

fix: honor wildcard-key custom converters when writing CSV (#1045) (#1056)#1069
Mikkey-f wants to merge 1 commit into
apache:mainfrom
Mikkey-f:feat/1045-csv-wildcard-converter

Conversation

@Mikkey-f

@Mikkey-f Mikkey-f commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What changed

Custom converters registered with supportExcelTypeKey() == null (the wildcard key (JavaType, null)) are now also registered under (JavaType, STRING). A single registerConverter(...) call now works for both xlsx and CSV writes.

Why

AbstractExcelWriteExecutor#doConvert resolves converters with the key (JavaType, targetCellDataType). The xlsx write path leaves the target type null, so a wildcard registration (JavaType, null) is found. The CSV write path forces the target type to STRING (csv is converted to string by default), so the same registration misses and the built-in string converter silently takes over: no warning is logged and the CSV output silently diverges from the xlsx output of the same code. See #1045 and #1056 (same root cause as EasyExcel#3054).

How

AbstractWriteHolder registers custom converters at two sites (own registration plus replay of workbook-level registrations when a sheet holder is created); both now delegate to a single putCustomConverter helper. When the declared excel type key is null, the converter is additionally registered under (JavaType, STRING) — the key the CSV lookup uses. No lookup code changes; the write path is untouched.

Impact

  • Without custom converters: unaffected (registration loop is not executed).
  • Explicit-key registrations: unaffected (registered under one key, as before).
  • Wildcard (null-key) registrations: CSV now applies the custom converter instead of silently falling back to the built-in string converter — matching the declared wildcard intent (matches all cell data types). The template-fill write path shares the same registry and benefits automatically.
  • Field-level converters (@ExcelProperty(converter = ...)) bypass the registry entirely and are unaffected.

Documentation

The null semantics of Converter#supportExcelTypeKey() are now documented on the method (javadoc) and on the website converter page (EN and zh-cn).

Tests

New WildcardConverterWriteTest (5 tests):

  • xlsx wildcard converter applies (regression guard)
  • CSV wildcard converter applies — red before the fix: output was true/false instead of the custom values
  • CSV without custom converters unchanged (still true/false)
  • explicit STRING-key registration unaffected
  • wildcard converter present under both keys in workbook and sheet holders (covers both registration sites, including sheet-level replay)

Red/green verified: the failing cases were reproduced against the pre-fix code, then turned green by the fix.

Full suite: 921/921 green; spotless and javadoc pass.

Fixes #1045
Fixes #1056

@Mikkey-f
Mikkey-f force-pushed the feat/1045-csv-wildcard-converter branch from 2b5190f to 68b4bb3 Compare September 7, 2026 06:20
@psxjoy
psxjoy requested a lite review from Copilot September 7, 2026 06:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The current wildcard registration logic can overwrite explicitly-registered (JavaType, STRING) converters depending on registration order, and one new test reads the “workbook” converter map via currentWriteHolder() which may not actually be the workbook holder after a write.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes a long-standing inconsistency where custom write converters registered with a wildcard excel-type key (supportExcelTypeKey() == null) were applied for XLSX writes but silently ignored for CSV writes (which force converter lookup under (JavaType, STRING)). The approach is to duplicate wildcard registrations under the CSV lookup key without changing the converter resolution logic.

Changes:

  • Register wildcard-key custom converters under both (JavaType, null) and (JavaType, STRING) during write-holder initialization.
  • Add a new regression test suite covering wildcard vs explicit converter behavior across XLSX/CSV and across workbook/sheet holder registration paths.
  • Document the supportExcelTypeKey() == null wildcard semantics in both EN and zh-cn docs and in the Converter javadoc.
File summaries
File Description
fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java Adds helper-based converter registration and duplicates wildcard registrations for CSV compatibility.
fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/Converter.java Expands javadoc to document wildcard semantics and CSV behavior.
fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/WildcardConverterWriteTest.java Adds regression tests for wildcard converter application and registration in holder maps.
website/docs/sheet/write/converter.md Documents wildcard-key behavior (EN).
website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/converter.md Documents wildcard-key behavior (zh-cn).
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +299 to +310
private void putCustomConverter(Converter<?> converter) {
getConverterMap()
.put(
ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
converter);
if (converter.supportExcelTypeKey() == null) {
getConverterMap()
.put(
ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), CellDataTypeEnum.STRING),
converter);
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the (JavaType, STRING) registration now only replaces the occupant while it is still the built-in default converter (identity-checked against DefaultConverterLoader.loadDefaultWriteConverter()), so an explicitly registered converter is never displaced regardless of registration order; supportExcelTypeKey() is cached in a local. Regression test csvExplicitStringKeyRegisteredBeforeWildcardStillWins covers the order you described.

Comment on lines +131 to +135
Map<ConverterKeyBuild.ConverterKey, Converter<?>> workbookMap =
excelWriter.writeContext().currentWriteHolder().converterMap();
Map<ConverterKeyBuild.ConverterKey, Converter<?>> sheetMap =
excelWriter.writeContext().writeSheetHolder().converterMap();
excelWriter.finish();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: workbookMap now reads writeWorkbookHolder().converterMap() — after write() the current holder is the sheet holder, so the two maps were aliased. The writer is now closed via try-with-resources.

@Mikkey-f
Mikkey-f force-pushed the feat/1045-csv-wildcard-converter branch from 68b4bb3 to 9f2782d Compare September 7, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants