如何使用Apache POI解析Word重复节内的内容控件?
解决Apache POI获取Word重复节内嵌套内容控件的问题
我完全懂你遇到的困扰——当处理包含嵌套内容控件的重复节SDT时,你现有的遍历逻辑只能拿到合并后的文本,没法捕获到独立的嵌套SDT节点。这是因为重复节(Repeating Section)的内部结构在OOXML里比较特殊,嵌套的SDT并不是直接作为顶级的IBodyElement存在的,而是藏在重复节的XWPFSDTContent容器里。
下面是调整后的解决方案,核心是递归遍历SDT的内部内容,同时补上对表格等元素的处理(你的原代码没覆盖表格场景,这也是遗漏点):
完整实现代码
import java.util.ArrayList; import java.util.List; import org.apache.poi.xwpf.usermodel.AbstractXWPFSDT; import org.apache.poi.xwpf.usermodel.IBodyElement; import org.apache.poi.xwpf.usermodel.IRunElement; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFSDT; import org.apache.poi.xwpf.usermodel.XWPFSDTContent; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; private static List<AbstractXWPFSDT> extractSDTsFromBodyElements(List<IBodyElement> elements) { List<AbstractXWPFSDT> sdts = new ArrayList<>(); for (IBodyElement e : elements) { if (e instanceof XWPFSDT) { XWPFSDT sdt = (XWPFSDT) e; sdts.add(sdt); // 递归处理当前SDT内部的所有嵌套SDT extractSDTsFromSDTContent(sdt.getContent(), sdts); } else if (e instanceof XWPFParagraph) { XWPFParagraph p = (XWPFParagraph) e; for (IRunElement e2 : p.getIRuns()) { if (e2 instanceof XWPFSDT) { XWPFSDT sdt = (XWPFSDT) e2; sdts.add(sdt); // 处理段落内SDT的嵌套内容 extractSDTsFromSDTContent(sdt.getContent(), sdts); } } } else if (e instanceof XWPFTable) { XWPFTable table = (XWPFTable) e; // 遍历表格的所有单元格 for (XWPFTableCell cell : table.getTableCells()) { // 递归处理单元格内的元素 sdts.addAll(extractSDTsFromBodyElements(cell.getBodyElements())); } } } return sdts; } // 辅助函数:遍历SDT内容里的所有元素,递归查找嵌套SDT private static void extractSDTsFromSDTContent(XWPFSDTContent content, List<AbstractXWPFSDT> sdts) { if (content == null) { return; } // 遍历SDT内容中的所有IBodyElement for (IBodyElement element : content.getBodyElements()) { if (element instanceof XWPFSDT) { XWPFSDT nestedSdt = (XWPFSDT) element; sdts.add(nestedSdt); // 继续递归处理嵌套SDT的内容 extractSDTsFromSDTContent(nestedSdt.getContent(), sdts); } else if (element instanceof XWPFParagraph) { XWPFParagraph p = (XWPFParagraph) element; for (IRunElement runElement : p.getIRuns()) { if (runElement instanceof XWPFSDT) { XWPFSDT inlineSdt = (XWPFSDT) runElement; sdts.add(inlineSdt); extractSDTsFromSDTContent(inlineSdt.getContent(), sdts); } } } else if (element instanceof XWPFTable) { XWPFTable table = (XWPFTable) element; for (XWPFTableCell cell : table.getTableCells()) { sdts.addAll(extractSDTsFromBodyElements(cell.getBodyElements())); } } } }
关键改动说明
递归处理SDT内部内容:
当捕获到一个XWPFSDT时,调用extractSDTsFromSDTContent函数深入其XWPFSDTContent容器,遍历里面的所有元素(段落、表格、嵌套SDT等),确保不会遗漏任何层级的嵌套控件。补充表格场景处理:
你的原代码只处理了段落和顶级SDT,而重复节里经常会包含表格,表格单元格内也可能有SDT,所以新增了表格的遍历逻辑,递归处理每个单元格内的元素。修复泛型定义:
原函数的List没有指定泛型,调整为List<AbstractXWPFSDT>,让类型更安全。
使用验证
调用这个修改后的函数后,你会发现集合里不仅包含顶级的重复节SDT,还会包含重复节内部所有嵌套的独立SDT节点,每个节点都能单独获取其属性和内容,而不是合并成一段文本。
内容的提问来源于stack exchange,提问作者Amanite Laurine




