Word 2003 XML列表转Docbook 5.1的XSLT实现难题
Word 2003 XML转Docbook 5.1:包裹连续列表项的XSLT解决方案
问题背景
需批量转换30份约80页的Word 2003 XML文档到Docbook 5.1,现有XSLT可将带<w:listPr>的段落转为<listitem>,但无法将连续同组的列表项用<itemizedlist>包裹;同时需保留列表项内的<InstrText>并转为Docbook的<indexterm>,不能丢失内容。
源文档片段(Word 2003 XML)
<w:p> <w:pPr> <w:pStyle w:val="2Standard"/> <w:listPr> <w:ilvl w:val="0"/> <w:ilfo w:val="14"/> <wx:t wx:val="·"/> <wx:font wx:val="Symbol"/> </w:listPr> </w:pPr> <w:r> <w:t>die Prognose der Wirtschaft</w:t> </w:r> <w:r> <w:fldChar w:fldCharType="begin"/> </w:r> <w:r> <w:instrText> XE "Wirtschaft"</w:instrText> </w:r> <w:r> <w:fldChar w:fldCharType="end"/> </w:r> </w:p> <w:p> <w:pPr> <w:pStyle w:val="2Standard"/> <w:listPr> <w:ilvl w:val="0"/> <w:ilfo w:val="14"/> <wx:t wx:val="·"/> <wx:font wx:val="Symbol"/> </w:listPr> </w:pPr> <w:r> <w:t>die Beratung der Politik.</w:t> </w:r> </w:p>
期望输出(Docbook 5.1)
<itemizedlist> <listitem> <para>die Prognose der Wirtschaft <indexterm><primary>Wirtschaft</primary></indexterm> </para> </listitem> <listitem> <para>die Beratung der Politik.</para> </listitem> </itemizedlist>
初始尝试的XSLT片段
<xsl:template match="w:p"> <xsl:choose> <xsl:when test="w:pPr/w:listPr/w:ilvl/@w:val = '0'"> <listitem> <para> <xsl:apply-templates select="w:r"/> </para> </listitem> </xsl:when> <xsl:otherwise> <para> <xsl:apply-templates/> </para> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="w:r"> <xsl:choose> <xsl:when test="w:instrText"> <indexterm> <primary> <xsl:apply-templates select="*/text()"/> </primary> </indexterm> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="w:t"/> </xsl:otherwise> </xsl:choose> </xsl:template>
解决方案:用for-each-group分组包裹列表项
核心思路是利用XSLT 2.0+的for-each-group功能,按相邻同层级、同列表ID的列表项分组,再用<itemizedlist>包裹每组。修改后的XSLT如下:
<!-- 匹配段落的父容器(如w:body,需根据实际文档结构调整) --> <xsl:template match="w:body"> <xsl:for-each-group select="w:p" group-adjacent=" if (self::w:p[w:pPr/w:listPr]) then concat(w:pPr/w:listPr/w:ilvl/@w:val, '|', w:pPr/w:listPr/w:ilfo/@w:val) else 'non-list' "> <xsl:choose> <!-- 处理连续列表项组 --> <xsl:when test="current-grouping-key() != 'non-list'"> <itemizedlist> <xsl:apply-templates select="current-group()"/> </itemizedlist> </xsl:when> <!-- 处理普通段落 --> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:template> <!-- 保留原有的w:p匹配模板,适配所有层级列表项 --> <xsl:template match="w:p"> <xsl:choose> <xsl:when test="w:pPr/w:listPr"> <listitem> <para> <xsl:apply-templates select="w:r"/> </para> </listitem> </xsl:when> <xsl:otherwise> <para> <xsl:apply-templates/> </para> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 优化w:r处理逻辑,精准提取索引项并跳过无效节点 --> <xsl:template match="w:r"> <xsl:choose> <xsl:when test="w:instrText"> <indexterm> <primary> <!-- 提取XE标记内的关键词,去除引号和多余空格 --> <xsl:value-of select="normalize-space(substring-before(substring-after(w:instrText, '"'), '"'))"/> </primary> </indexterm> </xsl:when> <!-- 跳过fldChar节点,避免输出空内容 --> <xsl:when test="w:fldChar"/> <xsl:otherwise> <xsl:apply-templates select="w:t"/> </xsl:otherwise> </xsl:choose> </xsl:template>
关键说明
- 分组逻辑:通过
group-adjacent将连续的、具有相同<w:ilvl>(列表层级)和<w:ilfo>(列表ID)的段落归为一组,确保同属一个列表的项被包裹在同一个<itemizedlist>中。 - 索引项优化:修改
<w:instrText>处理逻辑,精准提取XE后的关键词,避免保留多余的标记字符。 - 无效节点处理:新增对
<w:fldChar>的匹配规则,跳过该节点以避免输出无效空内容。
内容的提问来源于stack exchange,提问作者Pjoern
相关产品推荐
相关产品推荐

