2014-02-11 56 views
0

我有一個XML結構如下:XSLT 2.0:迭代,直到元件

<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()='Newline',但我不能完全弄清楚如何構造它。

在這裏,我想嘗試寫算法:

<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> 

回答

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元素等上。

+0

非常感謝你,我是不是意識到'xsl:for-each-group'中的'group-ending-with'屬性,但是這個功能完美無缺。 –

0

我的理解是,你的總體目標可以概括爲:

  1. 文字和大膽的元素應該被轉錄,後者與標籤標註的。

  2. 這些轉錄被分組在一起成爲一個單一的行元素。源代碼中的換行元素將這些組分開。

  3. 多個換行元素導致輸出一個空白行元素。

如果以上內容準確無誤,那麼您可以將您的輸入分解成組,其中元素組與其他元素組分開。然後處理每組分別根據規則您現在提出:

<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> 
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>