alibaba / easyexcel

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

调用 ExcelWriter 的 fill 方法时,如果传入的 data 参数内含的集合元素类型是 Map,且某些元素中缺失某些 key,就会导致生成的 Excel 文件中数据行错位 #3604

Open zhuermao opened 7 months ago

zhuermao commented 7 months ago

触发场景描述

调用 ExcelWriterfill 方法时,如果传入的 data 参数内含的集合元素类型是 Map,且某些元素中缺失某些 key,就会导致生成的 Excel 文件中数据行错位。

easyexcel 版本 3.3.3。

触发Bug的代码

public static void main(String[] args) {
    // 准备 10 行数据,基本上每行都有 aaa 和 bbb 两个 key,值分别为 a1 和 b1, a2 和 b2, ...
    // 唯独第 5 行,故意删除掉 bbb 这个 key
    List<Map<String, Object>> dataLineList = new ArrayList<>();
    for (int i = 1; i <= 10; i++) {
        Map<String, Object> m = new HashMap<>();
        m.put("aaa", "a" + i);
        m.put("bbb", "b" + i);
        if (i == 5) {
            m.remove("bbb");
        }
        dataLineList.add(m);
    }

    ExcelWriter excelWriter = EasyExcel.write("./run/result3.xlsx").withTemplate("./run/template3.xlsx").build();
    WriteSheet writeSheet = EasyExcel.writerSheet().build();
    FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.VERTICAL).build();
    excelWriter.fill(new FillWrapper("dataLine", dataLineList), fillConfig, writeSheet);
    excelWriter.writeContext().writeWorkbookHolder().getWorkbook().setForceFormulaRecalculation(true);
    excelWriter.finish();
}

上述代码准备了 10 行数据,基本上每行都有 aaabbb 两个 key,值分别为 a1b1, a2b2, ...。 唯独第 5 行,故意删除了 bbb 这个 key。

模板文件很简单:

image

输出结果文件中,从 a5 这一行开始发生了错位,b6 跑到了同一行来。

image

模板文件: template3.xlsx 输出结果文件: result3.xlsx

临时解决办法

我自行修改了 easyexcel 的源代码,主要是把 ExcelWriteFillExecutor 类中的 215 行以及 250 行给删除掉:

if (!dataKeySet.contains(variable)) {
    continue;
}

一些疑问和探讨

我对上述代码的写法不太理解。在 203 行,根据传入参数 Map 的 keySet 创建了 dataKeySet

Set<String> dataKeySet = new HashSet<>(dataMap.keySet());

然后这个 dataKeySet 的作用也仅仅就是后续 215 行以及 250 行的:

if (!dataKeySet.contains(variable)) {
    continue;
}

为什么要这么麻烦创建 dataKeySet?不能直接使用 dataMap.containsKey(variable) 吗?

如果直接使用 dataMap.containsKey(variable) 的话,我就不用修改 easyexcel 源代码了。我可以对传入的 Map 对象做个包装,包装成一个 containsKey 方法直接返回 true 的特殊 Map 对象,这样就能回避掉 215 行以及 250 行的检查了。像这样:

image

QYZX commented 2 months ago

为什么最新的release中不包含这个问题的修复?