XSLT 2.0:迭代直到元素

时间:2014-02-11 12:46:12

标签: xml xslt xpath

我有一个XML结构如下:

<document>
  <text>Here is some text </text>
  <bold>and this part is in bold</bold>
  <text> and this part is not.</text>
  <newline />
  <bold>foo foo foo</bold>
  <newline />
  <newline />
  <text>bar bar bar</text>
</document>

我想编写一个XSLT转换,它产生如下输出:

<document>
  <line>Here is some text <b>and this part is in bold</b> and this part is not.</line>
  <line><b>foo foo foo</b></line>
  <blankline>
  <line>bar bar bar</line>
</document>

我觉得这是一个问题,需要一个循环结构而不是文件/ *,直到跟随兄弟:name()=&#39; Newline&#39;,但我不能弄清楚如何构建它

这是我想尝试编写的算法:

<xsl:template match="document">
  <xsl:apply-templates select="*[1]"/>
</xsl:template>

<xsl:template match="text | bold">
  <line>
    <xsl:if test="name() = 'text'>
      <xsl:value-of select="." />
    </xsl:if>
    <xsl:if test="name() = 'bold'>
      <b><xsl:value-of select="." /></b>
    </xsl:if>

    <!-- I want a loop which runs over all following 'text' or 'bold' elements, but stops before the next Newline is reached -->
    <xsl:for-each select="preceding-sibling::*[self:newline]">
      <xsl:if test="name() = 'text'>
        <xsl:value-of select="." />
      </xsl:if>
      <xsl:if test="name() = 'bold'>
        <b><xsl:value-of select="." /></b>
      </xsl:if>
    </xsl:for-each>

    <!-- I then want to apply-templates on the next item (the newline) -->
    <xsl:apply-templates select="following-sibling" />
  </line>
</xsl:template>

<xsl:template match="newline">
  <!-- i need to work out how to ignore single <newline> here but output <blankline> if more than one <newline /> is together. -->
  <xsl:apply-templates select="following-sibling" />
</xsl:template>

3 个答案:

答案 0 :(得分:1)

当您使用XSLT 2.0时,最好的办法是使用xsl:for-each-group来解决您的问题。您需要对内容进行分组,以便每个组以newline元素结尾。然后,您可以简单地处理每个组中的元素:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

        <xsl:template match="document">
            <xsl:copy>
                <xsl:for-each-group select="*" group-ending-with="newline">
                    <!-- context node is set to the first element in the group -->
                    <xsl:apply-templates select="." mode="create-line"/>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>

        <!-- ignore any newline element where the next element is also a newline -->
        <xsl:template match="newline[following-sibling::*[1][self::newline]]"/>

        <!-- output a blankline element for the second of a pair of newlines -->
        <xsl:template match="newline[preceding-sibling::*[1][self::newline]]" mode="create-line">
            <blankline/>
        </xsl:template>

        <!-- use a wildcard to match and wrap the groups -->
        <xsl:template match="*" mode="create-line">
            <line><xsl:apply-templates select="current-group()"/></line>
        </xsl:template>

        <xsl:template match="bold">
            <b><xsl:apply-templates/></b>
        </xsl:template>

</xsl:stylesheet>

我使用了一种模式来简化节点组的处理 - 你可以简单地使用text元素的内置模板等等。

答案 1 :(得分:0)

我的理解是,您的总体目标可归纳为:

  1. 文本和粗体元素应该被转录,后者用标签注释。

  2. 这些转录组合在一起成为一个单独的行元素。源中的换行元素将这些组分开。

  3. 多个换行元素会导致空行元素的输出。

  4. 如果上述内容准确无误,那么您可以将输入分解为组,其中元素组与其他元素组分开。然后根据您提出的规则单独处理每个组:

    <xsl:for-each-group select="//node()" group-adjacent="boolean(name(.)='newline')">
            <xsl:choose>
                <xsl:when test="current-grouping-key()='true'">
                    <xsl:if test="count(current-group()) gt 1">
                        <blankline/>
                    </xsl:if>
                </xsl:when>
                <xsl:otherwise>
                    <line>
                        <!--Process text and bold elements here.-->
                    </line>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    

答案 2 :(得分:0)

如果您遇到XSLT 1.0,它会更棘手但可能:

<xsl:template match="document">
  <document>
    <xsl:apply-templates select="*[not(self::newline)][1]|newline/following-sibling::*[not(self::newline)][1]|newline/following-sibling::*[1][self::newline]" mode="first"/>
  </document>
</xsl:template>

<xsl:template match="newline" mode="first">
  <blankline />
</xsl:template>

<xsl:template match="*" mode="first">
  <line>
    <xsl:apply-templates select="self::*|following-sibling::*[generate-id(following-sibling::newline[1]) = generate-id(current()/following-sibling::newline[1])]" />
  </line>
</xsl:template>

<xsl:template match="text">
  <xsl:value-of select="." />
</xsl:template>

<xsl:template match="bold">
  <b>
    <xsl:value-of select="." />     
  </b>
</xsl:template>