Skip to content

[Bug] Header cells are converted with user-registered String converters, breaking @ExcelProperty(name) matching #1098

Open

Description

@Mikkey-f

Search before asking

  • I searched in the issues and found nothing similar.

Fesod version

main (2.1.0-incubating). Not tied to a recent change — the explicit-key case below reproduces on released versions too.

JDK version

17 (Temurin); not JDK-specific.

Operating system

Any

Steps To Reproduce

public static class Bean {
    @ExcelProperty("flag")
    private String flag;
    // getters / setters
}

public static class UpperCaseConverter implements Converter<String> {
    @Override public Class<?> supportJavaTypeKey() { return String.class; }

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

    @Override public String convertToJavaData(
            ReadCellData<?> cellData, ExcelContentProperty p, GlobalConfiguration g) {
        return cellData.getStringValue().toUpperCase();
    }
}

// file under test: header "flag", one data row with the value "abc"
FesodSheet.write(file, Bean.class).sheet().doWrite(Collections.singletonList(new Bean("abc")));

List<Bean> rows = FesodSheet.read(file, Bean.class, listener)
        .registerConverter(new UpperCaseConverter())
        .sheet()
        .doReadSync();

// rows.get(0).getFlag() is null

Current Behavior

The converter is invoked with the header cell ("flag") instead of the data cell ("abc"): convertToJavaData returns "FLAG", the framework then compares "FLAG" with the @ExcelProperty("flag") name, finds no matching column, and leaves the field null. No exception is raised, so the failure is silent.

The same path also fills AnalysisEventListener.invokeHeadMap(...), so the header map handed to listeners goes through user converters as well.

Using @ExcelProperty(index = 0) works, because matching then does not depend on the header text.

Expected Behavior

When the framework converts the head row to strings for name matching, header cells should be resolved against the built-in converters — user-registered converters are meant for data cells. invokeHeadMap should likewise receive the raw header text.

Note that in the failing case the converter is never invoked for the data cell either, so even a user who wants header conversion loses data conversion entirely.

Anything else?

Root cause: ConverterUtils.convertToStringMap resolves ConverterKeyBuild.buildKey(String.class, cellData.getType()) in the holder's converter map, which also contains user registrations. It is called from DefaultAnalysisEventProcessor.buildHead (header to field matching) and from AnalysisEventListener.invokeHead.

Found while reviewing #1086 (read-side wildcard converter fix). That PR does not introduce this — an explicit (String, STRING) registration already reproduces it on main. It does widen the set of affected registrations, since a wildcard Converter<String> gets expanded to (String, STRING) and would therefore be picked up by the header path too.

Proposed fix: resolve the header path against the built-in read converters (e.g. DefaultConverterLoader.copyDefaultReadConverter()) rather than the user-extended map. One decision point for the maintainers: whether invokeHeadMap should keep seeing user-converted text (a niche behaviour someone might rely on) or always see the raw header.

Happy to prepare the PR if the direction looks right — unless it is preferred to handle this within #1086.

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Activity

  1. bengbengbalabalabeng commented on Sep 12, 2026

    @bengbengbalabalabeng
    Contributor

    Based on my understanding, Converters should only apply to data cells and should not be involved in header parsing. Keeping the header as raw text ensures stable field matching and avoids the side effects of header conversion.

    This likely requires a new PR to handle it.

  2. Mayank-Sr commented on Sep 13, 2026

    @Mayank-Sr

    Is this issue available for contributions or is the already assigned?

  3. Mikkey-f commented on Sep 13, 2026

    @Mikkey-f
    ContributorAuthor

    Thanks for the interest! This one is already taken — I reported it and I'm preparing the PR myself (as noted under "Are you willing to submit a PR?"), and the author of #1086 has handed the header-path fix over to it as well.

    Sorry to turn you away — hope to see you on another issue soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions