tags:

views:

123

answers:

2

I have a xml doc with around 150 entries. I am sorting the entries in several fashions. One is alphabetical, which is displayed via XSLT and works perfectly, the others are by category and solution, which have issues with alternating banding color by row.

The problem arises when I iterate over entries that are not being displayed, it seems that they are being included in the count even though they are not being displayed. I asked this question once before under an anonymous user, hopefully I am clearer this time.

Thanks for the help.

XML Doc.

<case-studies>
    <!-- #### X #### -->
    <case-study> 
     <name>Entry 1</name>
     <category solution="Performance">Medical</category>
     <category solution="Medical">Security</category>
     <category solution="Industry">Medical</category>
     <category solution="A-Z">X</category>
    </case-study>

<!-- #### Y #### -->
    <case-study> 
     <name>Entry 2</name>
     <category solution="Industry">Education</category>
     <category solution="Convergence">Education</category>
     <category solution="A-Z">Y</category>
    </case-study>

</case-studies>

XSLT Call

    <%
  Dim mm_xsl As MM.XSLTransform = new MM.XSLTransform()
  mm_xsl.setXML(Server.MapPath("/data/xml/case-studies/case-studiesTest.xml"))
  mm_xsl.setXSL(Server.MapPath("/data/xslt/case-studies/categoryLandingOther.xsl"))
  mm_xsl.addParameter("solName", "Industry")
  mm_xsl.addParameter("catName", "Business services")
  Response.write(mm_xsl.Transform())
 %>

Portion of xslt

<xsl:for-each select="case-studies/case-study/category[. = $catName]">     

    <!--xsl:sort select="../name" /-->
    <xsl:if test="@solution[. = $solName]">


        <tr>
        <xsl:if test="(position() mod 2 = 1)">
      <xsl:attribute name="bgcolor">#e7e7e7</xsl:attribute>                
     </xsl:if>    
          <td class="cell1">                
          </td> 
          <td class="cell2" style="padding-top:2px;">» <a href="{../url}"><xsl:value-of select="../name"/></a></td>
          <td class="cell3">
            <xsl:for-each select="../solutionType">      
                <div class="clearRight"><xsl:value-of select="."/></div>
      </xsl:for-each>                
          </td>
        </tr>

    </xsl:if>
</xsl:for-each>
+2  A: 

First: Try to avoid <xsl:for-each>. It's a bad choice most of the time.

Second: Select only those nodes that you want to output and your row alternation will work:

<xsl:template match="/case-studies">
  <xsl:apply-templates select="case-study[
    category = $catName 
    and
    category/@solution = $solName
  ]">
    <xsl:sort select="name" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="case-study">
  <tr>
    <xsl:if test="position() mod 2 = 1"> 
      <xsl:attribute name="bgcolor">#e7e7e7</xsl:attribute>
    </xsl:if>    
    <td class="cell1" />                
    <td class="cell2" style="padding-top:2px;">
      <xsl:text>» </xsl:text>
      <a href="{url}"><xsl:value-of select="name"/></a>
    </td>
    <td class="cell3">
      <xsl:apply-templates select="solutionType" />
    </td>
  </tr>
</xsl:template>

<xsl:template match="solutionType">
  <div class="clearRight">
    <xsl:value-of select="."/>
  </div>
</xsl:template>

Edit:

You can use an <xsl:key> to speed up the selection process, but this will only have a positive effect if you query the same data repeatedly during the same transformation process.

<xsl:key name="kCaseStudy" 
         match="case-study" 
         use="concat(category, category/@solution)" 
/>

<!-- no need to be in "/case-studies" context this time -->
<xsl:template match="/">
  <xsl:apply-templates select="key('kCaseStudy', concat($catName, $solName))">
    <xsl:sort select="name" />
  </xsl:apply-templates>
</xsl:template>

<!-- ... code that uses "key('kCaseStudy', ...)" again ... -->
Tomalak
Awesome ........!
Cerebrus
Thank you, thank you, and thank you!
BillZ
+1  A: 

@Tomalak always beats me to it (with a better and more detailed answer)!

The problem in alternation of rows lies in two lines :

<xsl:for-each select="case-studies/case-study/category[. = $catName]">

and

<xsl:if test="@solution[. = $solName]">

What happens is that you select a node-set matching the above predicate condition. The processor remembers this for the rest of the loop. Then you apply another condition that further restricts the nodes processed (but not looped over).

You test the position within the loop using the position() function... which holds the total no. of nodes matched by the <xsl:for... loop (not excluding the nodes filtered out by the <xsl:if condition.)

A solution would be to combine the xsl:for-each and xsl:if conditions :

<xsl:for-each select="case-study/category[. = $catName and @solution = $solName]">

Of course, this explanation is just to illustrate the point. Tomalak's point about avoiding the use of for-each is very valid and his solution is awesome.

Cerebrus
+1 for explaining why the solution in the question does not work :)
Tomalak
BTW: Your profile says you are in India, that's halfway across the globe from here. In theory, you should be faster half of the day. ;-)
Tomalak
@Tomalak: I'm slower because the questions take longer to get here... the servers are in the U.S. :P :P
Cerebrus