2011-09-06 46 views
7

Web應用程序爲我提供了一個XML-feed,我無法更改。我想要做的是將此XML提要分成幾個無序列表。我試圖用下面的XSLT來做到這一點。XSLT將結果分組爲3

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >  
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8" /> 
    <xsl:param name="html-content-type" /> 
    <xsl:template match="/NavigationTree"> 
    <xsl:if test="count(//Page) > 0"> 
     <ul> 
     <xsl:apply-templates select="Page"> 
     </xsl:apply-templates> 
     </ul> 
    </xsl:if> 
    </xsl:template> 

    <xsl:template match="//Page">  
    <li class="{position() mod 3}"> 
     <xsl:text disable-output-escaping="yes"><![CDATA[»&nbsp;]]></xsl:text> 
     <a> 
     <xsl:attribute name="href"> 
      <xsl:value-of select="@FriendlyHref" disable-output-escaping="yes"/> 
     </xsl:attribute> 
     <xsl:value-of select="@MenuText" disable-output-escaping="no"/> 
     </a> 
    </li> 

    <xsl:if test="position() mod 3 = 0"> 
     <xsl:if test="position() &lt; count(//Page)"> 
     <!--Don't know if this is the correct approach, but when the position is 3 and there are more items following 
     I want to create an new unordered list--> 
     </xsl:if>  
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

使用XSLT在上面,我能夠把XML轉換成一個無序列表,在它6個項目(假設總有6個項目)。類似於下面的例子;

<ul> 
    <li>Item1</li> 
    <li>Item2</li> 
    <li>Item3</li> 
    <li>Item4</li> 
    <li>Item5</li> 
    <li>Item6</li> 
</ul> 

上面的例子是我現在得到的結果。但理想的結果是這樣的;

<ul> 
    <li>Item1</li> 
    <li>Item2</li> 
    <li>Item3</li> 
</ul> 
<ul> 
    <li>Item4</li> 
    <li>Item5</li> 
    <li>Item6</li> 
</ul> 

編輯 - 示例XML輸入

<NavigationTree> 
    <Settings> 
     <!--Snipped data--> 
    </Settings> 
    <Page ID="5" AreaID="1" MenuText="Bestellen" MouseOver="" Href="Default.aspx?ID=5" FriendlyHref="/nl-nl/klantenservice/bestellen.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="1" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
    <Page ID="6" AreaID="1" MenuText="Betalen" MouseOver="" Href="Default.aspx?ID=6" FriendlyHref="/nl-nl/klantenservice/betalen.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="2" LastInLevel="False" InPath="True" ChildCount="0" class="L2_Active" Active="True" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
    <Page ID="7" AreaID="1" MenuText="Retourneren" MouseOver="" Href="Default.aspx?ID=7" FriendlyHref="/nl-nl/klantenservice/retourneren.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="3" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
    <Page ID="8" AreaID="1" MenuText="Garantie" MouseOver="" Href="Default.aspx?ID=8" FriendlyHref="/nl-nl/klantenservice/garantie.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="4" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
    <Page ID="9" AreaID="1" MenuText="Faq" MouseOver="" Href="Default.aspx?ID=9" FriendlyHref="/nl-nl/klantenservice/veel-gestelde-vragen.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="5" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
    <Page ID="10" AreaID="1" MenuText="Contact" MouseOver="" Href="Default.aspx?ID=10" FriendlyHref="/nl-nl/klantenservice/contact.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" ShowInLegend="True" AbsoluteLevel="2" RelativeLevel="2" Sort="6" LastInLevel="True" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True"/> 
</NavigationTree> 

正如你可以看到我一直想在3個項目組輸出。這種HTML輸出可能使用XSLT嗎?如果是的話,我該怎麼做?歡迎任何幫助!

+0

提供樣本輸入。 –

+0

@Kirill。會做:) – Rob

+1

好問題,+1。請參閱我的答案以獲得兩種解決方案:一種不使用任何條件XSLT指令的XSLT 1.0解決方案;以及使用''更短的XSLT 2.0解決方案。對於一組更加通用的XSLT 1.0位置分組解決方案,它將一組不是兄弟姐妹的節點分組,看到我對這個問題的回答:http://stackoverflow.com/questions/7299594/grouping-and-sorting-xslt-by鍵和位置/ 7302747#7302747 –

回答

8

要做到這一點,你需要匹配元素,這是位置1,4,7,等等......換句話說,那裏位置()MOD 3等於1

<xsl:if test="position() mod 3 = 1"> 

這給出了列表的第一個元素。然後你可以在這剩下的2個元素,像這樣

<xsl:apply-templates select=".|following-sibling::Page[position() &lt; 3]" mode="list"/> 

乾脆把這個,給出了下面的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8"/> 
    <xsl:param name="html-content-type"/> 
    <xsl:param name="group-size" select="3"/> 
    <xsl:template match="/NavigationTree"> 
     <xsl:if test="count(//Page) &gt; 0"> 
     <xsl:apply-templates select="Page"/> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template match="Page"> 
     <xsl:if test="position() mod $group-size = 1"> 
     <ul> 
      <xsl:apply-templates select=".|following-sibling::Page[position() &lt; $group-size]" mode="list"/> 
     </ul> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template match="Page" mode="list"> 
     <li class="{position()}"> 
     <xsl:text disable-output-escaping="yes"><![CDATA[»&nbsp;]]></xsl:text> 
     <a> 
      <xsl:attribute name="href"> 
       <xsl:value-of select="@FriendlyHref" disable-output-escaping="yes"/> 
      </xsl:attribute> 
      <xsl:value-of select="@MenuText" disable-output-escaping="no"/> 
     </a> 
     </li> 
    </xsl:template> 
</xsl:stylesheet> 

當你輸入的XML運行,這應該生成以下

<ul> 
    <li class="1">»&nbsp;<a href="/nl-nl/klantenservice/bestellen.aspx">Bestellen</a></li> 
    <li class="2">»&nbsp;<a href="/nl-nl/klantenservice/betalen.aspx">Betalen</a></li> 
    <li class="3">»&nbsp;<a href="/nl-nl/klantenservice/retourneren.aspx">Retourneren</a></li> 
</ul> 
<ul> 
    <li class="1">»&nbsp;<a href="/nl-nl/klantenservice/garantie.aspx">Garantie</a></li> 
    <li class="2">»&nbsp;<a href="/nl-nl/klantenservice/veel-gestelde-vragen.aspx">Faq</a></li> 
    <li class="3">»&nbsp;<a href="/nl-nl/klantenservice/contact.aspx">Contact</a></li> 
</ul> 

請注意我已經參數化了組大小,例如,您可以輕鬆地將每個列表更改爲4或5個元素。

+0

+1偉大的解決方案。就像使用參數化組大小的解決方案一樣。良好的靈活性!感謝您的快速和良好的解決方案:) – Rob

+0

其實,我有一個不必要的** xsl:if **在我的解決方案! **位置()mod $ group-size = 1 **的測試需要在最初的** xsl:apply-templates select =「Page」**上進行。我相信這樣會更有效率。 –

3
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8" /> 

    <xsl:template match="/NavigationTree"> 
     <xsl:apply-templates select="Page[position() mod 3 = 1]" mode="ul"/> 
    </xsl:template> 

    <xsl:template match="Page" mode="li"> 
     <li class="{position() mod 3}"> 
      <a href="{@FriendlyHref}"> 
       <xsl:value-of select="@MenuText" disable-output-escaping="no"/> 
      </a> 
     </li> 
    </xsl:template> 

    <xsl:template match="Page" mode="ul"> 
     <ul> 
      <xsl:apply-templates select=". | following-sibling::Page[position() &lt; 3]" mode="li"/> 
     </ul> 
    </xsl:template> 

</xsl:stylesheet> 

輸出:

<ul> 
    <li class="1"> 
    <a href="/nl-nl/klantenservice/bestellen.aspx">Bestellen</a> 
    </li> 
    <li class="2"> 
    <a href="/nl-nl/klantenservice/betalen.aspx">Betalen</a> 
    </li> 
    <li class="0"> 
    <a href="/nl-nl/klantenservice/retourneren.aspx">Retourneren</a> 
    </li> 
</ul> 
<ul> 
    <li class="1"> 
    <a href="/nl-nl/klantenservice/garantie.aspx">Garantie</a> 
    </li> 
    <li class="2"> 
    <a href="/nl-nl/klantenservice/veel-gestelde-vragen.aspx">Faq</a> 
    </li> 
    <li class="0"> 
    <a href="/nl-nl/klantenservice/contact.aspx">Contact</a> 
    </li> 
</ul> 
+0

+1良好的工作解決方案。但是當我想改變顯示的項目數量時,我更喜歡來自Tim C的那個,因爲它具有更大的靈活性。 – Rob

+0

@Rob,不客氣! –

10

I. XSLT 1.0

下面是一個簡短的參數化解決方案,展示瞭如何按照預定義大小的組拆分兄弟元素。沒有明確的條件XSLT指令用於:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="pGroupSize" select="3"/> 

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

<xsl:template match="/*"> 
    <nums> 
    <xsl:apply-templates select= 
     "num[position() mod $pGroupSize = 1]"/> 
    </nums> 
</xsl:template> 

<xsl:template match="num"> 
    <group> 
    <xsl:copy-of select= 
    ".|following-sibling::* 
       [not(position() > $pGroupSize -1)]"/> 
    </group> 
</xsl:template> 
</xsl:stylesheet> 

當這種轉換應用於以下XML文檔

<nums> 
    <num>01</num> 
    <num>02</num> 
    <num>03</num> 
    <num>04</num> 
    <num>05</num> 
    <num>06</num> 
    <num>07</num> 
    <num>08</num> 
    <num>09</num> 
    <num>10</num> 
</nums> 

想要的,正確的結果產生

<nums> 
    <group> 
     <num>01</num> 
     <num>02</num> 
     <num>03</num> 
    </group> 
    <group> 
     <num>04</num> 
     <num>05</num> 
     <num>06</num> 
    </group> 
    <group> 
     <num>07</num> 
     <num>08</num> 
     <num>09</num> 
    </group> 
    <group> 
     <num>10</num> 
    </group> 
</nums> 

二, XSLT 2.0

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:param name="pGroupSize" select="3"/> 

<xsl:template match="/*"> 
    <nums> 
     <xsl:for-each-group select="*" 
      group-by="(position() -1) idiv $pGroupSize"> 
     <group> 
      <xsl:sequence select="current-group()"/> 
     </group> 
     </xsl:for-each-group> 
    </nums> 
</xsl:template> 
</xsl:stylesheet> 

當這種轉化是在同一個XML文檔(以上)應用中,相同的正確的結果產生

<nums> 
    <group> 
     <num>01</num> 
     <num>02</num> 
     <num>03</num> 
    </group> 
    <group> 
     <num>04</num> 
     <num>05</num> 
     <num>06</num> 
    </group> 
    <group> 
     <num>07</num> 
     <num>08</num> 
     <num>09</num> 
    </group> 
    <group> 
     <num>10</num> 
    </group> 
</nums> 

說明

  1. 使用<xsl:for-each-group>其中選定節點按序列分組l它們所屬的組的編號。

  2. 使用標準的XSLT 2.0功能current-group()

+1

+1提供xslt 2.0示例 – Rob

+0

@Rob:不客氣。 –