You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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, '&quot;'), '&quot;'))"/>
                </primary>
            </indexterm>
        </xsl:when>
        <!-- 跳过fldChar节点,避免输出空内容 -->
        <xsl:when test="w:fldChar"/>
        <xsl:otherwise>
            <xsl:apply-templates select="w:t"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

关键说明

  1. 分组逻辑:通过group-adjacent将连续的、具有相同<w:ilvl>(列表层级)和<w:ilfo>(列表ID)的段落归为一组,确保同属一个列表的项被包裹在同一个<itemizedlist>中。
  2. 索引项优化:修改<w:instrText>处理逻辑,精准提取XE后的关键词,避免保留多余的标记字符。
  3. 无效节点处理:新增对<w:fldChar>的匹配规则,跳过该节点以避免输出无效空内容。

内容的提问来源于stack exchange,提问作者Pjoern

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 07:50:18