You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apache POI实现XSLFTableRow到XSLFTable的复制?

问题描述

我已经成功用Apache POI实现了Word文档中XWPFTable的行添加/复制功能,相关代码如下:

public void addNewTemplateRowToTable(XWPFTableRow rowTemplate, XWPFTable table, int templateRowId, FormLetter formletter, int rowsCount, XWPFDocument document) {
    try {
        AtomicInteger rowCount = new AtomicInteger();
        //calculating the position to add a new row to the table.
        int newRowPosition = templateRowId+1;
        while(rowCount.get()<rowsCount) {
            XWPFTableRow oldRow = rowTemplate;
            CTRow ctrow = CTRow.Factory.parse(oldRow.getCtRow().newInputStream());
            XWPFTableRow newRow = new XWPFTableRow(ctrow, table);
            List<XWPFTableCell> cells = new ArrayList<XWPFTableCell>();
            newRow.getTableCells().forEach(cell-> {
                cells.add(cell);
            });
            for(XWPFTableCell cell : cells) {
                if(cell.getText().contains(HASH_DELIMITER)) {
                    String cellText = cell.getText().trim();
                    //split the text with # sign to get the key.
                    String fieldName = getFieldNameFromPlaceHolder(cellText);
                    String[] textArray = fieldName.split(HASH_DELIMITER);
                    String key = textArray[0];
                    FormLetterTableField tableField = (FormLetterTableField) formletter.getFormLetterFields().get(key);
                    List<FormLetterData> tableRowData = tableField.getTableRowData();
                    Map<String, Object> formletterFieldsRow = tableRowData.get(rowCount.get()).getFormLetterFields();
                    replaceXWPFTableCellValues(cell, formletterFieldsRow, document);
                } else if(cell.getText().startsWith(PLACEHOLDER_FIELD_PREFIX)&&cell.getText().endsWith(PLACEHOLDER_FIELD_SUFFIX)) {
                    Map<String, Object> fields = formletter.getFormLetterFields();
                    replaceXWPFTableCellValues(cell, fields, document);
                }
            }
            //adding new row to the table at newRowPosition.
            table.addRow(newRow, newRowPosition);
            newRowPosition+=1;
            rowCount.getAndIncrement();
        }
        table.removeRow(templateRowId);
    } catch(Exception e) {
        e.printStackTrace();
        log.error(e.getMessage(), e);
        return;
    }
}

现在需要处理PPT格式文件,为XSLFTable实现相同的行复制功能,但遇到了问题:XSLFTableRow没有getCTRow()方法,且构造器默认不可见、非公共,无法像创建XWPFTableRow那样直接实例化新行,恳请指导实现方法。

解决方案

针对XSLFTable的行复制,需要通过操作底层OOXML对象(CTRow、CTTable)实现,具体步骤如下:

1. 获取模板行的底层CTRow对象

XSLFTableRow虽无getCTRow()方法,但可从XSLFTable关联的CTTable中直接获取对应行的底层对象:

// 假设已获取目标表格和模板行索引
XSLFTable table = ...;
int templateRowId = ...;
XSLFTableRow templateRow = table.getRows().get(templateRowId);
CTTable ctTable = table.getCTTable();
CTRow templateCtRow = ctTable.getTrArray(templateRowId);

2. 复制CTRow并插入表格

由于无法直接实例化XSLFTableRow,需先复制CTRow插入到CTTable指定位置,再从XSLFTable中获取新行实例:

AtomicInteger rowCount = new AtomicInteger();
int newRowPosition = templateRowId + 1;
while (rowCount.get() < rowsCount) {
    // 深拷贝模板CTRow,避免修改新行影响模板
    CTRow newCtRow = CTRow.Factory.parse(templateCtRow.newInputStream());
    // 插入到CTTable的目标位置
    ctTable.insertTr(newRowPosition, newCtRow);
    // 从XSLFTable中获取刚添加的行
    XSLFTableRow newRow = table.getRows().get(newRowPosition);
    
    // 处理单元格内容替换(适配XSLFTableCell逻辑)
    List<XSLFTableCell> cells = new ArrayList<>(newRow.getCells());
    for (XSLFTableCell cell : cells) {
        String cellText = cell.getText().trim();
        if (cellText.contains(HASH_DELIMITER)) {
            String fieldName = getFieldNameFromPlaceHolder(cellText);
            String[] textArray = fieldName.split(HASH_DELIMITER);
            String key = textArray[0];
            FormLetterTableField tableField = (FormLetterTableField) formletter.getFormLetterFields().get(key);
            List<FormLetterData> tableRowData = tableField.getTableRowData();
            Map<String, Object> formletterFieldsRow = tableRowData.get(rowCount.get()).getFormLetterFields();
            replaceXSLFTableCellValues(cell, formletterFieldsRow);
        } else if (cellText.startsWith(PLACEHOLDER_FIELD_PREFIX) && cellText.endsWith(PLACEHOLDER_FIELD_SUFFIX)) {
            Map<String, Object> fields = formletter.getFormLetterFields();
            replaceXSLFTableCellValues(cell, fields);
        }
    }
    
    newRowPosition++;
    rowCount.getAndIncrement();
}
// 删除模板行
ctTable.removeTr(templateRowId);

3. 实现XSLFTableCell内容替换方法

XSLFTableCell的内容基于段落(XSLFTextParagraph),需适配其API实现替换逻辑:

private void replaceXSLFTableCellValues(XSLFTableCell cell, Map<String, Object> fields) {
    String cellText = cell.getText().trim();
    for (Map.Entry<String, Object> entry : fields.entrySet()) {
        String placeholder = PLACEHOLDER_FIELD_PREFIX + entry.getKey() + PLACEHOLDER_FIELD_SUFFIX;
        if (cellText.contains(placeholder)) {
            // 清空原有内容
            cell.clearText();
            // 添加新文本,可保留原模板样式(如需继承样式,可从模板单元格复制段落格式)
            XSLFTextParagraph p = cell.addNewTextParagraph();
            XSLFTextRun run = p.addNewTextRun();
            run.setText(entry.getValue().toString());
        }
    }
}

关键注意事项

  • XSLFTableRow实例只能通过XSLFTable.getRows()获取,无法直接new,必须先操作CTTable插入CTRow再获取
  • 复制CTRow时必须做深拷贝,否则新行和模板行会共享底层XML对象,修改时互相影响
  • XSLFTableCell的内容是段落级结构,替换时需清空原有段落再添加新内容,或直接修改现有段落文本

内容的提问来源于stack exchange,提问作者Furqan Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:15:55