[alibaba/easyexcel]修复当自定义 header 时,在 writeSheet 对象上指定字段名忽略不生效的问题 & 修复当属性被忽略时,会导致后续列后移,excel多了空白列的问题

2024-05-17 463 views
8
  1. 当使用自定义头时,如果使用ExcelWriterSheetBuilder指定的excludeColumnFiledNames是无效的,但是使用ExcelWriterBuilder是有效的
  2. 当类的属性被指定了 index,但是又被配置为忽略字段时,会导致实际生产的 Excel 列后移,感觉这里应该由顺位索引列往前补位比较合理

回答

1

非常感谢您的pr 格式化 有问题 到入包不允许出现 *

  1. 问题没听懂 也没看清楚改了啥
  2. index 代表定位 就按照位置走。order代表排序 order会自动偏移。
5

@zhuangjiaju

抱歉,之前没有描述清楚。

第二个问题:

当导出的类属性被指定为忽略字段时,会导致后续的列有多少个就会产生多少个空行。

@Data
public class IndexData {
    @ExcelProperty(value = "字符串标题", index = 0)
    private String string;
    @ExcelProperty(value = "日期标题", index = 1)
    private Date date;
    /**
     * 这里设置3 会导致第二列空的
     */
    @ExcelProperty(value = "数字标题", index = 2)
    private Double doubleData;
}
测试结果

当我导出类 IndexData 时,如果我指定忽略了 index 为 0 的属性,会导致生成 3 列空白列。

效果图如下: image

测试代码:
@Ignore
public class WriteTest1 {

    @Test
    public void indexWrite() {
        String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, IndexData.class)
            .sheet("模板")
            .excludeColumnIndexes(Collections.singleton(0))
            .doWrite(indexData());
    }

    private List<IndexData> indexData() {
        List<IndexData> list = new ArrayList<IndexData>();
        for (int i = 0; i < 10; i++) {
            IndexData data = new IndexData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
}
修改逻辑

此 PR 将逻辑调整为,当属性被忽略时,用户指定的角标位应往前移动,例如 IndexData 中的 属性 date对应的 index 为 1,在 excel 中位于第二列。 但是在使用时,如果忽略了 index 0,则实际生成时,doubleData 的 index 应该进一,在生成的 excel 中应该位于第一列才比较符合。

同时,如果用户指定角标不连续产生的空行,会进行保留。比如用户只指定了 index 0 和 index 2,则实际生成时,会导致第二列为空列

2
第一个问题:

主要是 使用 ExcelWriterBuilder 进行生成 excel 时,可以通过ExcelWriterBuilder#excludeColumnFiledNames 来进行列的排除, 但当我使用 ExcelWriterSheetBuilder 进行生成时,ExcelWriterSheetBuilder#excludeColumnFiledNames方法是不生效的。

测试对象
@Data
public class IndexData {
    @ExcelProperty(value = "字符串标题", index = 0)
    private String string;
    @ExcelProperty(value = "日期标题", index = 1)
    private Date date;
    @ExcelProperty(value = "数字标题", index = 2)
    private Double doubleData;
}
测试代码:
    /**
     * 自定义头信息下的列属性忽略测试
     */
    @Test
    public void ignoreTest() {
        // 文件名
        String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";

        // 忽略列属性
        Set<String> excludeColumnFiledNames = Collections.singleton("string");

        // 构建 excelWrite 对象
        ExcelWriterBuilder writerBuilder = EasyExcel.write().file(fileName);
        // 注意:在 excelWrite 对象中指定忽略的列属性是可以生效的
        // 1. writerBuilder.excludeColumnFiledNames(excludeColumnFiledNames);
        ExcelWriter writer = writerBuilder.build();

        // 构建 writeSheet 对象
        ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet("sheet-1");
        // 自定义头信息
        sheetBuilder.head(head());
        // 2. 这里指定忽略列属性不生效
        sheetBuilder.excludeColumnFiledNames(excludeColumnFiledNames);
        WriteSheet writeSheet = sheetBuilder.build();

        // 写入 excel
        writer.write(indexData(), writeSheet);
        writer.finish();
    }

    private List<IndexData> indexData() {
        List<IndexData> list = new ArrayList<IndexData>();
        for (int i = 0; i < 10; i++) {
            IndexData data = new IndexData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

    private List<List<String>> head() {
        List<List<String>> list = new ArrayList<List<String>>();
        List<String> head1 = new ArrayList<String>();
        head1.add("数字1" + System.currentTimeMillis());
        List<String> head2 = new ArrayList<String>();
        head2.add("日期1" + System.currentTimeMillis());
        list.add(head1);
        list.add(head2);
        return list;
    }
生成结果:

image

可以看到,明明已经指定了,string 属性生成时需要进行忽略,但是却没有效果

问题定位:

在 com.alibaba.excel.write.executor.ExcelWriteAddExecutor#initSortedAllFiledMapFieldList 方法中,进行了字段排序和忽略的控制,但是判断是否需要忽略时,写死了获取的 WriteHolder 是 WorkbookHolder,所以当我们使用 SheetWrite 进行指定忽略时就会失效。

 private void initSortedAllFiledMapFieldList(Class clazz, Map<Integer, Field> sortedAllFiledMap) {
        if (!sortedAllFiledMap.isEmpty()) {
            return;
        }
        WriteWorkbookHolder writeWorkbookHolder = writeContext.writeWorkbookHolder();
        boolean needIgnore =
            !CollectionUtils.isEmpty(writeWorkbookHolder.getExcludeColumnFiledNames()) || !CollectionUtils
                .isEmpty(writeWorkbookHolder.getExcludeColumnIndexes()) || !CollectionUtils
                .isEmpty(writeWorkbookHolder.getIncludeColumnFiledNames()) || !CollectionUtils
                .isEmpty(writeWorkbookHolder.getIncludeColumnIndexes());
        ClassUtils.declaredFields(clazz, sortedAllFiledMap,
            writeWorkbookHolder.getWriteWorkbook().getConvertAllFiled(), needIgnore, writeWorkbookHolder);
    }
修改方案:

首先获取的 WriteHolder 修改为 currentWriteHolder(),这样就可以在使用 SheetWriter 的之后正确获取到该对象。 再参考 com.alibaba.excel.metadata.property.ExcelHeadProperty#initColumnProperties 的方法,将 holder 强转为 AbstractWriteHolder 以便判断是否需要进行属性的忽略

    private void initSortedAllFiledMapFieldList(Class clazz, Map<Integer, Field> sortedAllFiledMap) {
        if (!sortedAllFiledMap.isEmpty()) {
            return;
        }

        // 获取当前的使用的 holder
        WriteHolder holder = writeContext.currentWriteHolder();
        boolean needIgnore = (holder instanceof AbstractWriteHolder) && (
            !CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getExcludeColumnFiledNames()) || !CollectionUtils
                .isEmpty(((AbstractWriteHolder) holder).getExcludeColumnIndexes()) || !CollectionUtils
                .isEmpty(((AbstractWriteHolder) holder).getIncludeColumnFiledNames()) || !CollectionUtils
                .isEmpty(((AbstractWriteHolder) holder).getIncludeColumnIndexes()));

        ClassUtils.declaredFields(clazz, sortedAllFiledMap,
            writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled(), needIgnore, holder);
    }
3

@zhuangjiaju 这个 pr 有考虑合并吗

5

oK