2011-03-16 100 views
2

我在XSLT一個小白XML節點列表創建HTML。 我有一個XML其中T節點,然後其他節點,然後又是T結點可能會再次出現之後節點再次,等等使用XSLT

<t /> 
<n1 /> 
<n2 /> 
.. 

<t/> 
<n3 /> 
<n4 /> 
... 

我需要把這個XML到什麼是HTML,其中t節點將它後面的所有節點包裝到下一個節點

<div class='t'> 
    <div class='n1'/> 
    <div class='n2'/> 
    ... 
</div> 

<div class='t'> 
    <div class='n3'/> 
    <div class='n4'/> 
    ... 
</div> 

我很難實現這一點。 任何想法\提示?

謝謝!

+0

您可以識別您支持的XSLT版本嗎?假設2.0你也可以使用分組功能。 – shannon 2011-03-16 00:51:34

回答

6

這是分組鄰道。有許多解決方案:

維特此簡潔(wellformed)輸入:

<root> 
    <t /> 
    <n1 /> 
    <n2 /> 
    <t/> 
    <n3 /> 
    <n4 /> 
</root> 

XSLT 1.0:與跟隨軸

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="root"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()[1]" mode="group"/> 
      <xsl:apply-templates select="t"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="t"> 
     <div class="t"> 
      <xsl:apply-templates select="following-sibling::node()[1]" 
           mode="group"/> 
     </div> 
    </xsl:template> 
    <xsl:template match="t" mode="group"/> 
    <xsl:template match="node()" mode="group"> 
     <xsl:apply-templates select="."/> 
     <xsl:apply-templates select="following-sibling::node()[1]" 
          mode="group"/> 
    </xsl:template> 
    <xsl:template match="*[starts-with(name(),'n')]"> 
     <div class="{name()}"/> 
    </xsl:template> 
</xsl:stylesheet> 

XSLT 1.0遍歷:鍵

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:key name="kNodeByMark" 
      match="node()[../t][not(self::t)]" 
      use="generate-id((..|preceding-sibling::t[1])[last()])"/> 
    <xsl:template match="root"> 
     <xsl:copy> 
      <xsl:apply-templates select="key('kNodeByMark',generate-id())"/> 
      <xsl:for-each select="t"> 
       <div class="t"> 
        <xsl:apply-templates 
         select="key('kNodeByMark',generate-id())"/> 
       </div> 
      </xsl:for-each> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*[starts-with(name(),'n')]"> 
     <div class="{name()}"/> 
    </xsl:template> 
</xsl:stylesheet> 

XSLT 2。0:for-each-group指令

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="root"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()[../t[1] >> .]"/> 
      <xsl:for-each-group select="node()" group-starting-with="t"> 
       <div class="t"> 
        <xsl:apply-templates 
         select="current-group()[position()>1]"/> 
       </div> 
      </xsl:for-each-group> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*[starts-with(name(),'n')]"> 
     <div class="{name()}"/> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

<root> 
    <div class="t"> 
     <div class="n1" /> 
     <div class="n2" /> 
    </div> 
    <div class="t"> 
     <div class="n3" /> 
     <div class="n4" /> 
    </div> 
</root> 

EDIT:遍歷跟隨軸重構看起來像其他的解決方案。剝離身份規則。

+0

+1。瞬間經典。 – Flack 2011-03-16 07:18:08

0

見我注意到關於你的問題,對於「哪個XSLT版本?」。如果您的目標版本支持分組,請參閱此處的其他答案,因爲這更容易理解,並且在任何XSLT處理器上幾乎肯定會表現得更好。如果您不確定,我建議您使用像這樣的1.0解決方案。

你可以完全像你貼與大多數XSLT處理器,但我添加了一個「根」元素的XML,以減少在回答你的問題的某些未知的「XML片段」做到這一點。

在下面這個解決方案,我試圖保持XSLT的形狀和你的願望輸出的形狀有直接關係。在我看來,這可以更容易地維護/理解,至少對於較小的樣式表來說。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/root"> 
    <xsl:for-each select="t"> 
     <div class='t'> 
     <xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]"> 
      <div class='{name()}' /> 
     </xsl:for-each> 
     </div> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

的「後續兄弟右手側:: * [計數=(計數(當前()/前同輩:: T)+ 1)(::叔兄弟前述),而不是(self :: t)]「可以簡化,我敢肯定,使用像」current():: position()「(這是無效的,fyi),但我生疏,不記得一些別名語法。

這基本上表示:1)評估與T的相同量的每T. 2)選擇的元素在它們之前,如我們目前正在評估的T的指數。

請注意,您可能已嘗試通過程序迭代,並發現無法存儲XSLT中找到的最後一個值。或者你已經發現你可以,但只能使用嵌套模板。您正在執行的這種相同類型的數據透視有許多XSLT新手觸碰路障,所以不要感覺不好。