0
点赞
收藏
分享

微信扫一扫

easyexcel 导出引发单字母属性名引发的血案

三次方 03-13 15:30 阅读 1

项目场景:

提示:这里简述项目相关背景:

例如:项目场景:easyexcel 导出属性名单字母小写导出excel内容为空


问题描述

例如:easyexcel 导出属性名单字母小写引发的问题

@ExcelProperty(value = "T+30")
@ColumnWidth(30)
private Integer tPlus30;  // 单字母属性,有数据值但是写excel 为空

原因分析:

例如:`net.sf.cglib.beans.BeanMap 属性名引发的问题
源码

private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List<Field> fieldList) {
        WriteHolder currentWriteHolder = writeContext.currentWriteHolder();
        ####  重点BeanMap对象转化引发的问题        
				BeanMap beanMap = BeanMap.create(oneRowData);

        Set<String> beanMapHandledSet = new HashSet<String>();
        int cellIndex = 0;
        // If it's a class it needs to be cast by type
        if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) {
            Map<Integer, Head> headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
            Map<Integer, ExcelContentProperty> contentPropertyMap =
                writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap();
            for (Map.Entry<Integer, ExcelContentProperty> entry : contentPropertyMap.entrySet()) {
                cellIndex = entry.getKey();
                ExcelContentProperty excelContentProperty = entry.getValue();
                String name = excelContentProperty.getField().getName();
                if (writeContext.currentWriteHolder().ignore(name, cellIndex)) {
                    continue;
                }
                if (!beanMap.containsKey(name)) {
                    continue;
                }
                Head head = headMap.get(cellIndex);
                WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);
                Cell cell = WorkBookUtil.createCell(row, cellIndex);
                WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE);
                Object value = beanMap.get(name);
                CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell,
                    value, excelContentProperty, head, relativeRowIndex);
                WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE);
                beanMapHandledSet.add(name);
            }
        }
        // Finish
        if (beanMapHandledSet.size() == beanMap.size()) {
            return;
        }
        if (cellIndex != 0) {
            cellIndex++;
        }
        Map<String, Field> ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap();
        initFieldList(oneRowData.getClass(), fieldList);
        for (Field field : fieldList) {
            String filedName = field.getName();
            boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName)
                || ignoreMap.containsKey(filedName) || writeContext.currentWriteHolder().ignore(filedName, cellIndex);
            if (uselessData) {
                cellIndex++;
                continue;
            }
            Object value = beanMap.get(filedName);
            WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE);
            Cell cell = WorkBookUtil.createCell(row, cellIndex++);
            WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE);
            CellData cellData = converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell,
                value, null, null, relativeRowIndex);
            WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
        }
    }

注意:commons-beanutils 的 BeanMap 和 cglib 一样的问题,虽然类型没问题但是属性名还是不对。
原因:
调试一下就会发现,问题出在 BeanInfo 里面 PropertyDescriptor 的 name 不正确。
java.beans.Introspector#getBeanInfo() 中
经过分析会发现 java.beans.Introspector#getTargetPropertyInfo 方法是字段解析的关键
不管get或者set 属性会自动截取3
然后去构造 PropertyDescriptor:

PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException {
        if (bean == null) {
            throw new IntrospectionException("Target Bean class is null");
        }
        setClass0(bean);
        setName(Introspector.decapitalize(base));
        setReadMethod(read);
        setWriteMethod(write);
        this.baseName = base;
    }

Introspector.decapitalize(base) 方法:

从代码中我们可以看出 (1) 当 name 的长度 > 1,且第一个字符和第二个字符都大写时,直接返回参数作为PropertyDescriptor name。 (2) 否则将 name 转为首字母小写. 这种处理本意是为了不让属性为类似 URL 这种缩略词转为 uRL ,结果“误伤”了我们这种场景。


解决方案:

最简单的办法属性不用单字母

举报

相关推荐

0 条评论