XSLT将开放XML转换为干净的HTML列表

时间:2018-04-17 03:00:37

标签: html xslt openxml

我创建了一个XSLT文件,它将Word XML中的所有内容转换为干净的HTML,但是我无法正确地隐藏嵌套列表。

我将v16.12文件保存为XML。 Word文件包含两个列表

这是导出的Open XML(仅与子弹有关)。

<w:body>
<w:p w:rsidR="00875AF6" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="0"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 1 level 1</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="0"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 2 level 1</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 3 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="2"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 4 level 3</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="2"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 5 level 3</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 6 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="2"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 7 level 3</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="007A38EC">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="0"/>
            <w:numId w:val="1"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 1 Bullet 8 level 1</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241"/>
<w:p w:rsidR="00575241" w:rsidRDefault="00575241" w:rsidP="00575241">
    <w:r>
        <w:t>This is a break</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="00575241" w:rsidRDefault="00575241" w:rsidP="00575241"/>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="0"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 1 level 1</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 2 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="2"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 3 level 3</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="0"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 4 level 1</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 5 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 6 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="2"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 7 level 3</w:t>
    </w:r>
    <w:bookmarkStart w:id="0" w:name="_GoBack"/>
    <w:bookmarkEnd w:id="0"/>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241">
    <w:pPr>
        <w:pStyle w:val="ListParagraph"/>
        <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="2"/>
        </w:numPr>
    </w:pPr>
    <w:r>
        <w:t>List 2 Bullet 8 level 2</w:t>
    </w:r>
</w:p>
<w:p w:rsidR="007A38EC" w:rsidRDefault="007A38EC" w:rsidP="00575241"/>
<w:sectPr w:rsidR="007A38EC" w:rsidSect="00D678D3">
    <w:pgSz w:w="11900" w:h="16840"/>
    <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"
        w:header="708" w:footer="708" w:gutter="0"/>
        <w:cols w:space="708"/>
        <w:docGrid w:linePitch="360"/>
    </w:sectPr>
</w:body>

使用XSLT我需要将XML转换为HTML

<ul>
  <li>List 1 Bullet 1 level 1</li>
  <li>List 1 Bullet 2 level 1
    <ul>
      <li>List 1 Bullet 3 level 2
        <ul>
          <li>List 1 Bullet 4 level 3</li>
          <li>List 1 Bullet 5 level 3</li>
        </ul>
      </li>
      <li>List 1 Bullet 6 level 2
        <ul>
          <li>List 1 Bullet 7 level 3</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>List 1 Bullet 8 level 1</li>
</ul>
<p>This is a gap</p>
<ul>
  <li>List 2 Bullet 1 level 1
    <ul>
      <li>List 2 Bullet 2 level 2
        <ul>
          <li>List 2 Bullet 3 level 3</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>List 2 Bullet 4 level 1
    <ul>
      <li>List 2 Bullet 5 level 2</li>
      <li>List 2 Bullet 6 level 2
        <ul>
          <li>List 2 Bullet 7 level 3</li>
        </ul>
      </li>
      <li>List 2 Bullet 8 level 2</li>
    </ul>
  </li>
</ul>

我已经研究过,我发现最接近的是使用函数和for-each-group,如下所示。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" version="2.0"
    exclude-result-prefixes="xs mf">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:function name="mf:group" as="node()*">
        <xsl:param name="nodes" as="node()*"/>
        <xsl:param name="level" as="xs:integer"/>
        <xsl:if test="$nodes">
            <list type="ul">
                <xsl:for-each-group select="$nodes"
                    group-adjacent="boolean(self::*[@level = $level])">
                    <xsl:choose>
                        <xsl:when test="current-grouping-key()">
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </list>
        </xsl:if>
    </xsl:function>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@*, node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="item[@level]">
        <item>
            <xsl:apply-templates/>
        </item>
    </xsl:template>

    <xsl:template match="test">
        <xsl:copy>
            <xsl:for-each-group select="*" group-adjacent="boolean(self::item)">
                <xsl:choose>
                    <xsl:when test="current-grouping-key()">
                        <xsl:sequence select="mf:group(current-group(), 0)"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

不幸的是,使用函数和for-each-groups超出了我的能力范围。我的问题是如何修改上面的XSLT以使用我从Word获得的XML?

1 个答案:

答案 0 :(得分:1)

首先,我们将从身份模板开始:

<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@*, node()"/>
    </xsl:copy>
</xsl:template>

其次,我们必须匹配根节点w:body并使用xsl:for-each-group对元素进行分组。之后,我们将节点存储在变量(firstpass)中,以便稍后进一步操作节点,例如:

<!-- If you want to specify the target node (1 in 22 as you say),
     you can adjust the xpath below to match your target node.
-->
<xsl:template match="w:body">
    <xsl:variable name="firstPass">
        <xsl:for-each-group select="*" group-adjacent="boolean(self::w:p[descendant::w:ilvl])">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <!-- the zero (0) was obtained from the value of
                         w:val attribute of w:ilvl node -->
                    <xsl:sequence select="mf:group(current-group(), 0)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:variable>
    <xsl:apply-templates select="$firstPass/node()"/>
</xsl:template>

我们可以调整你提到的功能。我们可以将与组相邻的目标节点修改为

<xsl:function name="mf:group" as="node()*">
    <xsl:param name="nodes" as="node()*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:if test="$nodes">
        <ul>
            <xsl:for-each-group select="$nodes"
                group-adjacent="boolean(self::*[descendant::w:ilvl/@w:val = $level])">
                <xsl:choose>
                    <xsl:when test="current-grouping-key()">
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </ul>
    </xsl:if>
</xsl:function>

以下是清理所需的模板

<xsl:template match="w:p">
    <xsl:apply-templates select="descendant::w:t"/>
</xsl:template>

<xsl:template match="w:p[.='']|w:sectPr"/>

<xsl:template match="w:t">
    <xsl:choose>
        <xsl:when test="ancestor::w:p[descendant::w:pStyle[@w:val='ListParagraph']]">
            <li>
                <xsl:apply-templates/>
            </li>
        </xsl:when>
        <xsl:otherwise>
            <p>
                <xsl:apply-templates/>
            </p>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

之后,我们仍然需要将<ul>次级别插入父级<li>。要做到这一点,我们必须进行第二次转型。

我们现在将匹配firstpass变量

中存在的节点
<xsl:template match="li[following-sibling::*[1][name()='ul']]">
    <xsl:copy>
        <xsl:apply-templates/>
        <!-- this will copy the target ul nodes, albeit in a different mode -->
        <xsl:apply-templates select="following-sibling::*[1][name()='ul']" mode="transfer"/>
    </xsl:copy>
</xsl:template>

<!-- this will delete the target node -->
<xsl:template match="ul[preceding-sibling::*[1][name()='li']]"/>

和其他模式的身份模板

<xsl:template match="@* | node()" mode="transfer">
    <xsl:copy>
        <xsl:apply-templates select="@*, node()"/>
    </xsl:copy>
</xsl:template>

整个样式表如下:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    xmlns:w="www.wnamespace.com"
    version="2.0"
    exclude-result-prefixes="xs mf w">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:function name="mf:group" as="node()*">
        <xsl:param name="nodes" as="node()*"/>
        <xsl:param name="level" as="xs:integer"/>
        <xsl:if test="$nodes">
            <ul>
                <xsl:for-each-group select="$nodes"
                    group-adjacent="boolean(self::*[descendant::w:ilvl/@w:val = $level])">
                    <xsl:choose>
                        <xsl:when test="current-grouping-key()">
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </ul>
        </xsl:if>
    </xsl:function>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@*, node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@* | node()" mode="transfer">
        <xsl:copy>
            <xsl:apply-templates select="@*, node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="w:p">
        <xsl:apply-templates select="descendant::w:t"/>
    </xsl:template>

    <xsl:template match="w:p[.='']|w:sectPr"/>

    <xsl:template match="w:t">
        <xsl:choose>
            <xsl:when test="ancestor::w:p[descendant::w:pStyle[@w:val='ListParagraph']]">
                <li>
                    <xsl:apply-templates/>
                </li>
            </xsl:when>
            <xsl:otherwise>
                <p>
                    <xsl:apply-templates/>
                </p>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

<xsl:template match="w:body">
    <xsl:variable name="firstPass">
        <xsl:for-each-group select="*" group-adjacent="boolean(self::w:p[descendant::w:ilvl])">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <xsl:sequence select="mf:group(current-group(), 0)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:variable>
    <xsl:apply-templates select="$firstPass/node()"/>
</xsl:template>

    <xsl:template match="ul[preceding-sibling::*[1][name()='li']]"/>

</xsl:stylesheet>

在行动here中查看。