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

Avatar of undefined
Last Comment
Jammerules

8/22/2022 - Mon
SOLUTION
Ken Butters

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
Ken Butters

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.
Gertone (Geert Bormans)

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
Gertone (Geert Bormans)

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
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!
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
Jammerules

ASKER
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

Gertone (Geert Bormans)

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
Jammerules

ASKER
...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!
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.