From 29b96cd4d8daa67c9dc82f117928548b6cc3a5f1 Mon Sep 17 00:00:00 2001 From: CodeMan-cmd <2291415248@qq.com> Date: Wed, 23 Sep 2026 09:23:55 +0800 Subject: [PATCH] fix: filter columns by java field name for camel case field names The column filter compared the name kept by the cglib bean map of a field with the field names configured through includeColumnFieldNames/excludeColumnFieldNames. For a field whose name starts with a lower case letter followed by an upper case letter, for example xRealIp or pName, the two names differ, so includeColumnFieldNames dropped the column and excludeColumnFieldNames kept it. The reorder by include list also assigned a position to an unknown column name, which shifted the following columns. --- .../apache/fesod/sheet/util/ClassUtils.java | 35 ++-- .../sheet/converter/CamelCaseFieldData.java | 55 ++++++ .../converter/CamelCaseFieldDataTest.java | 176 ++++++++++++++++++ 3 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java index 3116e9bf9..897553bc0 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java @@ -343,7 +343,7 @@ private static FieldCache doDeclaredFields(Class clazz, ConfigurationHolder c FieldWrapper field = entry.getValue(); // The current field needs to be ignored - if (writeHolder.ignore(field.getFieldName(), entry.getKey())) { + if (writeHolder.ignore(field.getField().getName(), entry.getKey())) { ignoreSet.add(field.getFieldName()); // indexFieldMap is keyed by the field's explicit @ExcelProperty(index), which for // explicit-index fields equals the sortedFieldMap position (entry.getKey()); remove @@ -384,26 +384,31 @@ private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) Collection includeColumnFieldNames = writeHolder.includeColumnFieldNames(); if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { - // Field sorted map + // Field sorted map, keyed by the Java field name, which is what the user configures Map filedIndexMap = MapUtils.newHashMap(); - int fieldIndex = 0; - for (String includeColumnFieldName : includeColumnFieldNames) { - filedIndexMap.put(includeColumnFieldName, fieldIndex++); - } + fieldCache + .getSortedFieldMap() + .forEach( + (index, field) -> filedIndexMap.put(field.getField().getName(), index)); // rebuild sortedFieldMap Map tempSortedFieldMap = MapUtils.newHashMap(); - fieldCache.getSortedFieldMap().forEach((index, field) -> { - Integer tempFieldIndex = filedIndexMap.get(field.getFieldName()); - if (tempFieldIndex != null) { - tempSortedFieldMap.put(tempFieldIndex, field); + int fieldIndex = 0; + for (String includeColumnFieldName : includeColumnFieldNames) { + Integer index = filedIndexMap.get(includeColumnFieldName); + // The name is not a field of the class, the column is ignored, it must not hold a position + if (index == null) { + continue; + } + FieldWrapper field = fieldCache.getSortedFieldMap().get(index); + tempSortedFieldMap.put(fieldIndex, field); - // The user has redefined the ordering and the ordering of annotations needs to be invalidated - if (!tempFieldIndex.equals(index)) { - indexFieldMap.remove(index); - } + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (fieldIndex != index) { + indexFieldMap.remove(index); } - }); + fieldIndex++; + } fieldCache.setSortedFieldMap(tempSortedFieldMap); return; } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java new file mode 100644 index 000000000..3ba83e7d7 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldData.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.fesod.sheet.annotation.ExcelProperty; + +/** + * Data model whose field names start with a lower case letter followed by an upper case letter. + * + *

Field names such as {@code xRealIp} are common in generated DTOs, for example when a column + * named {@code X_REAL_IP} is converted to camel case. The name kept by the cglib bean map of such a + * field differs from the Java field name, which is why these names are covered explicitly. + */ +@Getter +@Setter +@EqualsAndHashCode +public class CamelCaseFieldData { + @ExcelProperty("name") + private String name; + + @ExcelProperty("xRealIp") + private String xRealIp; + + @ExcelProperty("pName") + private String pName; + + @ExcelProperty("attackType") + private String attackType; +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java new file mode 100644 index 000000000..f0aaaa95e --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/CamelCaseFieldDataTest.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converter; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; + +/** + * Tests column include/exclude for field names that start with a lower case letter followed by an + * upper case letter, for example {@code xRealIp} or {@code pName}. + * + *

The name kept by the internal cglib bean map of such a field is not the Java field name, so + * filtering by field name must not be based on that name. + */ +@Tag(Tags.ROUND_TRIP) +class CamelCaseFieldDataTest extends AbstractExcelTest { + + @ParameterizedTest + @ExcelFormatSource + void writeAllCamelCaseFields(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class).sheet().doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + Assertions.assertEquals("pName1", record.get(2)); + Assertions.assertEquals("attackType1", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void writeHeadOfCamelCaseFields(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class).sheet().doWrite(data()); + List> dataMap = + FesodSheet.read(file).headRowNumber(0).sheet().doReadSync(); + Assertions.assertEquals(2, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name", record.get(0)); + Assertions.assertEquals("xRealIp", record.get(1)); + Assertions.assertEquals("pName", record.get(2)); + Assertions.assertEquals("attackType", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeColumnFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "xRealIp", "pName", "attackType")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + Assertions.assertEquals("pName1", record.get(2)); + Assertions.assertEquals("attackType1", record.get(3)); + } + + @ParameterizedTest + @ExcelFormatSource + void includePartOfCamelCaseFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "xRealIp")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("xRealIp1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void excludeCamelCaseFieldNames(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .excludeColumnFieldNames(Arrays.asList("xRealIp", "attackType")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeUnknownColumnFieldName(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "notExist", "pName")) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + @ParameterizedTest + @ExcelFormatSource + void includeUnknownColumnFieldNameOrderByIncludeColumn(ExcelFormat format) throws Exception { + File file = createTempFile(format); + FesodSheet.write(file, CamelCaseFieldData.class) + .includeColumnFieldNames(Arrays.asList("name", "notExist", "pName")) + .orderByIncludeColumn(true) + .sheet() + .doWrite(data()); + List> dataMap = FesodSheet.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("name1", record.get(0)); + Assertions.assertEquals("pName1", record.get(1)); + } + + private List data() { + List list = new ArrayList(); + CamelCaseFieldData data = new CamelCaseFieldData(); + data.setName("name1"); + data.setXRealIp("xRealIp1"); + data.setPName("pName1"); + data.setAttackType("attackType1"); + list.add(data); + return list; + } +}