XSLT转换Open XML为整洁HTML列表:嵌套列表转换失败求助
解决Word Open XML嵌套列表转HTML的XSLT问题
嘿,你遇到的嵌套列表转换问题在处理Word Open XML时挺常见的——主要是因为Word的列表结构依赖w:numPr里的ilvl(层级)和numId(列表标识),而XSLT需要主动跟踪这些值来正确生成嵌套的<ul>/<ol>结构。
先拆解你给出的XML片段里的关键信息:
<w:pStyle w:val="ListParagraph"/>标记这是一个列表段落<w:ilvl w:val="0"/>表示这是第0级(顶级)列表项<w:numId w:val="1"/>表示属于ID为1的列表
之前你的XSLT可能没有处理层级切换的逻辑,导致嵌套列表无法正确闭合或开启。下面是一个针对性的解决方案:
核心思路
- 跟踪当前的列表ID(
current-numId)和层级(current-ilvl),每次遇到新的列表段落时对比这两个值 - 当层级加深时,嵌套新的
<ul>/<ol>;层级变浅时,关闭对应数量的列表标签 - 同一列表ID下的不同层级要保持列表类型一致(比如项目符号对应
<ul>,编号对应<ol>)
完整XSLT示例代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> <!-- 全局变量跟踪当前列表状态 --> <xsl:variable name="current-numId" select="''"/> <xsl:variable name="current-ilvl" select="-1"/> <!-- 匹配文档根节点 --> <xsl:template match="/"> <html> <body> <xsl:apply-templates select="w:document/w:body/*"/> </body> </html> </xsl:template> <!-- 处理普通段落 --> <xsl:template match="w:p[not(w:pPr/w:numPr)]"> <p> <xsl:apply-templates select="w:t"/> </p> </xsl:template> <!-- 处理列表段落 --> <xsl:template match="w:p[w:pPr/w:numPr]"> <xsl:variable name="numId" select="w:pPr/w:numPr/w:numId/@w:val"/> <xsl:variable name="ilvl" select="number(w:pPr/w:numPr/w:ilvl/@w:val)"/> <!-- 实际场景可通过w:numbering节点判断是项目符号还是编号列表 --> <xsl:variable name="is-bullet" select="true()"/> <!-- 切换列表ID时,关闭之前的所有列表 --> <xsl:if test="$numId != $current-numId"> <xsl:call-template name="close-lists"> <xsl:with-param name="level" select="$current-ilvl"/> </xsl:call-template> <xsl:variable name="current-numId" select="$numId"/> </xsl:if> <!-- 处理层级加深的情况 --> <xsl:if test="$ilvl > $current-ilvl"> <xsl:call-template name="open-lists"> <xsl:with-param name="start-level" select="$current-ilvl + 1"/> <xsl:with-param name="end-level" select="$ilvl"/> <xsl:with-param name="is-bullet" select="$is-bullet"/> </xsl:call-template> </xsl:if> <!-- 处理层级变浅的情况 --> <xsl:if test="$ilvl < $current-ilvl"> <xsl:call-template name="close-lists"> <xsl:with-param name="level" select="$current-ilvl"/> <xsl:with-param name="target-level" select="$ilvl"/> </xsl:call-template> </xsl:if> <!-- 输出列表项内容 --> <li> <xsl:apply-templates select="w:t"/> </li> <!-- 更新当前层级变量 --> <xsl:variable name="current-ilvl" select="$ilvl"/> </xsl:template> <!-- 辅助模板:递归打开多层嵌套列表 --> <xsl:template name="open-lists"> <xsl:param name="start-level"/> <xsl:param name="end-level"/> <xsl:param name="is-bullet"/> <xsl:if test="$start-level <= $end-level"> <xsl:choose> <xsl:when test="$is-bullet"><ul></xsl:when> <xsl:otherwise><ol></xsl:otherwise> </xsl:choose> <xsl:call-template name="open-lists"> <xsl:with-param name="start-level" select="$start-level + 1"/> <xsl:with-param name="end-level" select="$end-level"/> <xsl:with-param name="is-bullet" select="$is-bullet"/> </xsl:call-template> </xsl:if> </xsl:template> <!-- 辅助模板:递归关闭多层嵌套列表 --> <xsl:template name="close-lists"> <xsl:param name="level"/> <xsl:param name="target-level" select="-1"/> <xsl:if test="$level > $target-level"> </ul> <xsl:call-template name="close-lists"> <xsl:with-param name="level" select="$level - 1"/> <xsl:with-param name="target-level" select="$target-level"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet>
关键细节说明
- 状态跟踪:通过全局变量记录当前的列表ID和层级,确保每次处理列表段落时能精准对比层级变化
- 递归处理嵌套:用
open-lists和close-lists两个递归模板处理多层嵌套的开启与关闭,避免冗余代码 - 列表类型适配:示例里用
is-bullet简化处理,实际可以读取Word文档中<w:numbering>节点的定义,自动判断生成<ul>还是<ol>
测试你的片段
针对你给出的顶级列表项(ilvl="0"),这段XSLT会输出:
<ul> <li>[你的段落文本]</li> </ul>
如果后续有ilvl="1"的嵌套项,会自动生成:
<ul> <li>[顶级文本] <ul> <li>[嵌套文本]</li> </ul> </li> </ul>
记得补充列表类型判断的逻辑(读取w:numbering节点),这样处理不同类型的列表会更准确!
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

