Skip to content

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

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

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