如何使用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
相关产品推荐
相关产品推荐

