Link to home
Start Free TrialLog in
Avatar of Jammerules
Jammerules

asked on

Q: XSL - Using For-Each, Choose functions

Experts,

Noob question - I am trying to do a simple exercise with For-Each and Choose functions of XSL. I want to select a particular value based on a condition. Here are my XSL and XML codes:
XSL:
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"> 
  <xsl:output method="html"/>
    
  <xsl:template match="/">
      <html>
          <table>              
              <tr>
                  <td bgcolor="lightgray"> Republican President </td>
                  <td>
                      <xsl:for-each select="Profiles/Profile"> 
                          <xsl:choose>
                              <xsl:when test="./Party='GOP'">
                                 <xsl:value-of select="./FirstName"/>
                                 <xsl:text> - (2001-2009) </xsl:text>
                              </xsl:when> 
                              <xsl:otherwise>
                                  <xsl:value-of select="./FirstName"/>
                                  <xsl:text> DEM </xsl:text>
                              </xsl:otherwise>
                          </xsl:choose>   
                      </xsl:for-each>
                  </td>
              </tr>
              <!--
              <tr>
                  <td> Democratic President </td>
                  <td bgcolor="gray">
                      <xsl:for-each select="Profiles/Profile">
                          <xsl:if test="./Party='DEM'">
                              <xsl:value-of select="./FirstName"/>
                          </xsl:if>
                      </xsl:for-each>
                  </td>
              </tr>  
              -->                     
          </table>
      </html>
  </xsl:template> 

</xsl:stylesheet>
 

Open in new window

XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="XSLTFile_ex1.xslt"?>
<Profiles>
      <Profile>
          <Salutation>Mr. </Salutation>  
          <FirstName>Barack</FirstName>
          <LastName>Obama
              <Suffixes>
                  <Suffix></Suffix> 
              </Suffixes>
          </LastName>
          <Party>DEM</Party>

          <Salutation>Mr. </Salutation>
          <FirstName>George</FirstName>
          <LastName>
              Bush
              <Suffixes>
                  <Suffix>Sr</Suffix>
              </Suffixes>
          </LastName>
          <Party>GOP</Party>

      </Profile>
</Profiles>

Open in new window


I am unable to get the required result. Where am I going wrong?
SOLUTION
Avatar of Ken Butters
Ken Butters
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Note:

<xsl:apply-templates select="//Profile[.//Party='GOP']"/>

The apply templates path above is saying.... go find the routine that matches ="//Profile[.//Party='GOP']"/>

Further that XPath means... for each Profile node in the entire document, that contains a child node of Party, and the value of the Party is "GOP".

Hope that helps.
My two cts.

The XML you have as the source could well be processed using a much more complex XSLT, but I think you should follow buttersk advice to wrap each president in its own profile

buttersk however gives the wrong input on what this XPath: Profile[.//Party='DEM']" means
the .//Party in the predicate is referring to all descendants from the current context,
this includes the children, but also deeper descendants. On some non optimised processords this will break performance. If you want children you need a single "/" instead of a double "//"
Profile[./Party='GOP']"
or the shorter form
Profile[Party='GOP']"

since Profiles is you root, put it in the starting template
<xsl:template match = "Profiles">
instead of
<xsl:template match = "/">
This has the advantage that the processor does not need to dig down the entire tree for a Profile element
<xsl:apply-templates select="Profile[Party='GOP']"/>
instead of
<xsl:apply-templates select="//Profile[.//Party='GOP']"/>

You should NEVER have a "//" starting the match statement of a template
<xsl:template match="//Profile[.//Party='DEM']">
should be
<xsl:template match="Profile[Party='DEM']">

All these changes will serve your performance well
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jammerules
Jammerules

ASKER

Buttersk and Gertone,

Thanks so much for your answers and explanations. So far, in my learning process, I only used <xsl :template> for applying styles for a specific element. I did not know that you could also use a condition with it - e.g., "Profile[Party='DEM']". Now, I know :).

At the same time, since I started the exercise with <xsl:choose> and <xsl:when>, can one of you show me the same exercise with them, please? Thanks!
Belay that! :)

I have got it working. Output kinda looks silly, but I'm learning. Meh!

For those interested:

XSL:
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"> 
  <xsl:output method="html"/>
    
  <xsl:template match="Profiles/Profile">
      <html>
          <table>              
              <tr>
                  <td bgcolor="lightred" > Republican President </td>
                  <td>
                      <xsl:choose>
                          <xsl:when test="./Party='GOP'">
                              <xsl:value-of select="./FirstName"/>
                              <xsl:text> - GOP </xsl:text>
                          </xsl:when> 
                      </xsl:choose>
                  </td>
              </tr> 
              <tr>
                  <td  bgcolor="lightgreen"> Democratic President </td>
                  <td>
                      <xsl:choose>
                          <xsl:when test="./Party='DEM'">
                              <xsl:value-of select="./FirstName"/>
                              <xsl:text> - DEMS </xsl:text>
                          </xsl:when> 
                      </xsl:choose>
                  </td>
              </tr>                    
          </table>
      </html>
  </xsl:template> 
</xsl:stylesheet>

Open in new window

XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="XSLTFile_ex1.xslt"?>
<Profiles>
      <Profile>
          <Salutation>Mr. </Salutation>  
          <FirstName>Barack</FirstName>
          <LastName>Obama
              <Suffixes>
                  <Suffix></Suffix> 
              </Suffixes>
          </LastName>
          <Party>DEM</Party>
      </Profile>
      <Profile>
          <Salutation>Mr. </Salutation>
          <FirstName>Richard</FirstName>
          <LastName>
              Nixon
              <Suffixes>
                  <Suffix></Suffix>
              </Suffixes>
          </LastName>
          <Party>GOP</Party>
      </Profile>
      <Profile>
          <Salutation>Mr. </Salutation>
          <FirstName>Bill</FirstName>
          <LastName>
              Clinton
              <Suffixes>
                 <Suffix></Suffix>
              </Suffixes>
          </LastName>
          <Party>GOP</Party>
      </Profile>
     <Profile>
         <Salutation>Mr. </Salutation>
         <FirstName>George</FirstName>
         <LastName>
             Bush
             <Suffixes>
                 <Suffix>Jr</Suffix>
             </Suffixes>
         </LastName>
         <Party>GOP</Party>
     </Profile>
</Profiles>

Open in new window

Have a look at this one
(I see that you introduced the xsl:text correctly :-)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0"> 
        <xsl:output method="html"/>
        
        <xsl:template match="Profiles">
            <html>
                <table>              
                    <tr>
                        <td bgcolor="lightred" > Republican President </td>
                    </tr>
                    <xsl:apply-templates select="Profile[Party='GOP']"/>
                    <tr>
                        <td  bgcolor="lightgreen"> Democratic President </td>
                    </tr>                    
                    <xsl:apply-templates select="Profile[not(Party='GOP')]"/>
                </table>
            </html>  
        </xsl:template>
        
        <xsl:template match="Profile">
            <tr>
                <td>
                    <xsl:value-of select="FirstName"/>
                    <xsl:choose>
                        <xsl:when test="Party='GOP'">
                            <xsl:text> - GOP </xsl:text>
                        </xsl:when> 
                        <xsl:otherwise>
                            <xsl:text> - DEMS </xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </td>
            </tr>
        </xsl:template> 
        
    </xsl:stylesheet>
</xsl:stylesheet>

Open in new window


Note that I removed some paths to shorter form, please learn to do so
And no need to have
"Profiles/Profile" in a match attribute of a template if the only context for Profile is Profiles,
no extra value, just slow down
...if the only context for Profile is Profiles...

Understood. Thanks again for all that you guys do on here. I can't imagine the difficulty for starters like me without forums like this!