当 header 的单元格包含换行符时,Apache POI createTable 生成损坏的文件

Apache POI createTable generates corrupted file when a header's cell contains a line break

我正在使用 Apache POI 4.1.2 在 Java 中创建 Excel 个文件。我有一段代码可以从现有的单元格创建一个 table,并且一切正常,直到我在 header 的单元格中有一个换行符。

之后我尝试更改 table 的列名称,但没有解决任何问题。

下面是重现问题的最短代码:

    public void test() throws IOException {
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet();

        // headers
        XSSFRow headersRow = sheet.createRow(0);
        headersRow.createCell(0).setCellValue("Column1");
        headersRow.createCell(1).setCellValue("Column2");

        // a second row
        XSSFRow row = sheet.createRow(1);
        row.createCell(0).setCellValue(1);
        row.createCell(1).setCellValue(2);

        // create a table
        AreaReference area = wb.getCreationHelper().createAreaReference(
                new CellReference(sheet.getRow(0).getCell(0)),
                new CellReference(sheet.getRow(1).getCell(1))
        );
        XSSFTable table = sheet.createTable(area);

        // styling (no problem here)
        sheet.setColumnWidth(0, 5000);
        sheet.setColumnWidth(1, 5000);
        CTTable cttable = table.getCTTable();
        cttable.addNewTableStyleInfo();
        XSSFTableStyleInfo style = (XSSFTableStyleInfo) table.getStyle();
        style.setName("TableStyleMedium6");
        style.setShowColumnStripes(false);
        style.setShowRowStripes(true);
        cttable.addNewAutoFilter().setRef(area.formatAsString());
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setWrapText(true);
        headersRow.getCell(0).setCellStyle(cellStyle);

        // this file is OK
        try (FileOutputStream outputStream = new FileOutputStream("C:\tmp\test.xlsx")) {
            wb.write(outputStream);
        }

        // add a line break in a header's cell
        headersRow.getCell(0).setCellValue("Column1\nwith a line break");
        // this file has a problem
        try (FileOutputStream outputStream = new FileOutputStream("C:\tmp\test2.xlsx")) {
            wb.write(outputStream);
        }

        // this doesn't fix anything
        table.getColumns().get(0).setName("Column1");
        try (FileOutputStream outputStream = new FileOutputStream("C:\tmp\test3.xlsx")) {
            wb.write(outputStream);
        }

        // neither does this
        cttable.getTableColumns().getTableColumnList().get(0).setName("Column1");
        try (FileOutputStream outputStream = new FileOutputStream("C:\tmp\test4.xlsx")) {
            wb.write(outputStream);
        }
    }

Excel 正确加载 text.xlsx,但抱怨所有其他文件:
我们发现某些内容存在问题...

Excel修复文件后,一切正常,但我想去掉警告信息。

如有任何帮助,我们将不胜感激。 谢谢

这是 XSSFTable.updateHeaders 的一个错误。在写入 table 的 XML 时调用此方法。这是因为 table 列名称始终必须与单元格内容同步。例如,如果单元格内容是“Column1”并且此单元格是 table 的列 header,则此 table 的列名称也必须是“Column1”(XML: <tableColumn id="1" name="Column1"/>).

但是对于 headers 列中的换行,有一个特殊之处。如果单元格内容是“Column1\nwith一个换行符”并且这个单元格是一个table的列header,那么这个table的列名必须是XML 为 <tableColumn id="1" name="Column1_x000a_with a line break"/>。所以“\n”被替换为“x000a”。此外,“\r”必须替换为“x000d”。这是因为“\r\n”换行不会有XML.

中换行的意思

所以 XSSFTable.java - updateHeaders 必须这样修补,即“\n”被“x000a”替换,“\r”被“[=30”替换=]x000d".

...
    public void updateHeaders() {
        XSSFSheet sheet = (XSSFSheet)getParent();
        CellReference ref = getStartCellReference();
        if (ref == null) return;

        int headerRow = ref.getRow();
        int firstHeaderColumn = ref.getCol();
        XSSFRow row = sheet.getRow(headerRow);
        DataFormatter formatter = new DataFormatter();

        if (row != null && row.getCTRow().validate()) {
            int cellnum = firstHeaderColumn;
            CTTableColumns ctTableColumns = getCTTable().getTableColumns();
            if(ctTableColumns != null) {
                for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
                    XSSFCell cell = row.getCell(cellnum);
                    if (cell != null) {
                        String colName = formatter.formatCellValue(cell);
                        colName = colName.replace("\n", "_x000a_");
                        colName = colName.replace("\r", "_x000d_");
                        col.setName(colName);
                    }
                    cellnum++;
                }
            }
        }
        tableColumns = null;
        columnMap = null;
        xmlColumnPrs = null;
        commonXPath = null;
    }
...

由于 XSSFTable.updateHeaders 被调用而 table 的 XMLXSSFWorkbook.write 被写入,因此除了修补此方法之外别无他法。没有任何机会改变 table 的 XMLXSSFWorkbook.write.