Search code examples
xmlxsltoxygenxml

XSLT checking values in multiple nodes


I have the following XML, which I need to extract the param value based on a child node attribute using XSLT. In this case extract a list of the activities my students are part of:

XML

<students>
   <student id="1000020001"/>  
   <student id="1000020002"/>      
</students>   
<activities>
    <activity name="yoga beginners" start="2016-10-12" end="2016-12-17">          
         <members>            
              <member id="1000020001"/>              
         </members>
    </activity>
    <activity name="yoga intermediate" start="2017-10-12" end="2017-12-17">          
         <members>            
              <member id="1000020001"/>  
              <member id="1000020002"/>            
         </members>
    </activity>
</activities>

I want to create an XSLT which display the activities my students are part of, in this case I have:

XSLT For each student:

<xsl:for-each select="/activities/activity">
   <b>Activity:</b>: 
   <xsl:call-template name="extractActivities">
      <xsl:with-param name="student-id" select="@id"/>
   </xsl:call-template>            
</xsl:for-each>

<xsl:template name="extractActivities">
    <xsl:param name="student-id"/>
    <xsl:if test="$student-id = /activities/activity/members/member/@id">
        <xsl:value-of select="@name"/>
    </xsl:if>
</xsl:template>

I have the following two problems:

  1. First XSLT displays "Activity:" twice as is doing the loop for each node.
  2. No activity name is displayed

Solution

  • It is best to resolve cross-references by using a key:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="UTF-8" />
    
    <xsl:key name="act" match="activity" use="members/member/@id" />
    
    <xsl:template match="/root">
        <html>
            <body>
                <xsl:for-each select="students/student">
                    <h3>Student: <xsl:value-of select="@id" /></h3>
                    <xsl:for-each select="key('act', @id)">
                        <b>Activity: </b><xsl:value-of select="@name" />
                        <br/>
                    </xsl:for-each>
                </xsl:for-each>
            </body>
        </html>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to the following well-formed input:

    XML

    <root>
       <students>
          <student id="1000020001"/>
          <student id="1000020002"/>
       </students>
       <activities>
          <activity name="yoga beginners" start="2016-10-12" end="2016-12-17">
             <members>
                <member id="1000020001"/>
             </members>
          </activity>
          <activity name="yoga intermediate" start="2017-10-12" end="2017-12-17">
             <members>
                <member id="1000020001"/>
                <member id="1000020002"/>
             </members>
          </activity>
       </activities>
    </root>
    

    the result will be:

    <html>
    <body>
    <h3>Student: 1000020001</h3>
    <b>Activity: </b>yoga beginners<br>
    <b>Activity: </b>yoga intermediate<br>
    <h3>Student: 1000020002</h3>
    <b>Activity: </b>yoga intermediate<br>
    </body>
    </html>