返回
简化 Docx4J 报表生成:高效处理表格和多行数据
java
2024-11-24 20:22:32
简化 Docx4J 生成报表
使用 Docx4J 从带有占位符的 Word 模板生成报表,看似简单,但实际操作中可能遇到不少坑。本文将介绍如何利用 Docx4J 更高效地处理报表生成,特别是包含表格和多行数据的情况。
问题
假设有一个 Word 模板,页眉包含占位符${creationDate}
,正文包含一个表格,表格内容如下:
ID | First Name | Last Name | Age | Gender |
-----------------------------------------------------------
${id} | ${firstName} | ${lastName} | ${age} | ${gender} |
我们需要用 Java 程序读取这个模板,并将占位符替换成实际数据。表格中的数据可能包含多行,需要动态生成多行表格内容。
常规解决方案与优化
通常,我们会使用 Docx4J 的 VariableReplace
功能替换简单的占位符。但是,对于表格中的多行数据,这种方法就显得力不从心了。更有效的方法是利用 Docx4J 的 XML 处理能力直接操作 Word 文档的底层 XML 结构。
步骤一:解析模板
首先,加载 Word 模板并将其转换为 org.docx4j.wml.Document
对象。
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("template.docx"));
步骤二:替换页眉占位符
可以使用 VariableReplace
处理简单的占位符,例如页眉中的 ${creationDate}
。
HashMap<String, String> mappings = new HashMap<>();
mappings.put("creationDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
VariablePrepare.prepare(wordMLPackage); // 预处理,确保占位符可替换
wordMLPackage.getMainDocumentPart().variableReplace(mappings);
步骤三:处理表格数据
关键在于如何处理表格中的多行数据。我们需要找到表格占位符所在的行,然后根据数据循环生成新的行,并将新行插入到表格中。
List<Object> tables = getAllElementFromObject(wordMLPackage.getMainDocumentPart(), Tbl.class);
for (Object tbl : tables) {
List<Object> rows = getAllElementFromObject(tbl, Tr.class);
for (Object tr : rows) {
if (containsPlaceholder(tr)) { // 判断该行是否包含占位符
int index = rows.indexOf(tr); // 获取占位符所在行的索引
List<Map<String, String>> data = getData(); // 获取需要填充的数据
for (Map<String, String> rowData : data) {
Tr newRow = XmlUtils.deepCopy((Tr) tr); // 复制占位符所在的行作为新行
replacePlaceholdersInRow(newRow, rowData); // 替换新行中的占位符
rows.add(index + 1, newRow); // 将新行插入到表格中
index++; // 更新索引
}
rows.remove(tr); // 删除原始的占位符行
break; // 处理完一个表格就跳出
}
}
}
// ... containsPlaceholder, replacePlaceholdersInRow, getData 和 getAllElementFromObject 的实现方法详见下方
辅助函数示例:
private boolean containsPlaceholder(Object obj) {
// ... 使用正则表达式或其他方法判断对象是否包含占位符
}
private void replacePlaceholdersInRow(Tr row, Map<String, String> data) {
// ... 遍历 row 中的 Tc (单元格) ,并使用 data 中的值替换占位符
}
private List<Map<String, String>> getData() {
// ... 模拟从数据源获取数据
}
private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
if (toSearch.isAssignableFrom(obj.getClass())) {
result.add(obj);
} else if (obj instanceof ContentAccessor) {
List<?> children = ((ContentAccessor) obj).getContent();
for (Object child : children) {
result.addAll(getAllElementFromObject(child, toSearch));
}
}
return result;
}
步骤四:保存文档
最后,将修改后的文档保存到新的文件中。
wordMLPackage.save(new File("output.docx"));
安全建议
- 确保模板文件的来源可靠,避免恶意代码注入。
- 对用户输入的数据进行校验和转义,防止 XSS 攻击。
通过上述方法,我们可以更灵活地控制报表生成过程,并高效地处理多行数据。希望本文能帮助开发者简化 Docx4J 报表生成的操作。