alibaba / easyexcel

快速、简洁、解决大文件内存溢出的java处理Excel工具
https://easyexcel.opensource.alibaba.com
Apache License 2.0
32.09k stars 7.5k forks source link

当我使用EasyExcel写入文件时,若实体类字段名为A,B等列名时,无法写入该列 #3913

Closed Wangyisimida closed 1 month ago

Wangyisimida commented 1 month ago

触发场景描述

easyexcel version>3.3.2

触发Bug的代码

    @Test
    public void writeTest() {
        // 写入文件
        String outputFile = "E:\\FE需求\\CSB财管报资产注册\\output.xlsx";
        // 这里 需要指定写用哪个class去写
        try (com.alibaba.excel.ExcelWriter excelWriter = EasyExcel.write(outputFile, CsbDataItemTest.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("output").build();
            excelWriter.write(genData(), writeSheet);
        }
        log.info("write file success");
    }

    private List<CsbDataItemTest> genData() {
        List<CsbDataItemTest> list = new ArrayList<CsbDataItemTest>();
        CsbDataItemTest item = new CsbDataItemTest();
        item.setField1("云订单");
        item.setA("comment");
        item.setField3(10);
        item.setD(10);
        list.add(item);
        list.add(item);
        log.info("the size : {}", list.size());
        return list;
    }

@Data
@EqualsAndHashCode
@ToString
public class CsbDataItemTest {
    @ExcelProperty("业务对象")
    private String field1 = "云订单";

    @ExcelProperty("数据标准IT编码")
    private String A;

    @ExcelProperty("长度")
    private Integer field3;

    @ExcelProperty("精度")
    private Integer D;

}

导出结果展示

image

psxjoy commented 1 month ago

The results indeed appear to have some issues. I'll attempt to reproduce and resolve the problem. If anyone else experiences similar issues or has any solutions, please feel free to share and discuss!

psxjoy commented 1 month ago

EasyExcel uses CGLIB to convert beans to map data types during write operations. CGLIB eventually calls the java.beans.Introspector#decapitalize method, which has its own parsing rules. This can cause properties like 'A' to be converted to 'a' through reflection, leading to data mismatches. You can either rename the property or convert it to lowercase to resolve the issue.

easyexcel在写入时,使用cglib将bean转换成map类型的数据。cglib最后调用的是java.beans.Introspector#decapitalize 方法。它有一套属于自己的解析规则,这导致类似'A'这种属性,反射的结果成为了'a',最终导致无法正常匹配数据写入。你可以修改属性名称,或者将其改为小写。

/**
 * Utility method to take a string and convert it to normal Java variable
 * name capitalization.  This normally means converting the first
 * character from upper case to lower case, but in the (unusual) special
 * case when there is more than one character and both the first and
 * second characters are upper case, we leave it alone.
 * <p>
 * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
 * as "URL".
 *
 * @param  name The string to be decapitalized.
 * @return  The decapitalized version of the string.
 */
public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}
psxjoy commented 1 month ago

I attempted to use reflection directly to retrieve the data, which should resolve your issue. However, switching from CGLIB to reflection is a significant change, and project managers need to carefully consider various factors before deciding whether to merge it. I strongly recommend using standard camelCase for property names. Alternatively, you can refer to the reflection method, replace the create() function in BeanMapUtils, and temporarily compile the jar file for use.

直接使用反射获取数据能解决你的问题。但是从cglib转换到反射,这种重要的转变需要考虑很多情况后,才会决定是否合并。推荐使用正常的驼峰形式命名属性。你也可以参考反射的使用方法,在BeanMapUtils中替换create()函数,临时编译对应的jar包使用。

psxjoy commented 1 month ago

846

1514

1467

1594

1609

2627