XSLT split result in groups of 3

前端 未结 3 2249
执笔经年
执笔经年 2020-12-16 04:46

An web-application is providing me an XML-feed, which I can\'t change. What I want to do is split this XML-feed into several unordered lists. I\'m trying to do this with the

相关标签:
3条回答
  • 2020-12-16 05:25
    <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>
    

    Output:

    <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 讨论(0)
  • 2020-12-16 05:34

    I. XSLT 1.0 .

    Here is a short and parameterized solution showing how to split siblings elements in groups of predefined size. No explicit conditional XSLT instructions are used:

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

    when this transformation is applied on the following XML document:

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

    the wanted, correct result is produced:

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

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

    When this transformation is applied on the same XML document (above), the same correct result is produced:

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

    Explanation:

    1. Use of <xsl:for-each-group> where the selected nodes are grouped by the sequential number of the group they belong to.

    2. Use of the standard XSLT 2.0 function current-group().

    0 讨论(0)
  • 2020-12-16 05:44

    To do this, you need to match on the Page element that it is position 1, 4, 7, etc... In other words, where there position() mod 3 equals 1.

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

    This gives the first element of the list. You can then get the remaining 2 elements in the this, like so

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

    Putting this altogether, gives the following 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>
    

    When run on your input XML, this should generate the following output

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

    Do note I have parameterised the group size, allowing you to easily change to 4 or 5 elements per list, for example.

    0 讨论(0)
提交回复
热议问题