Chloraphil
Chloraphil

Reputation: 2849

Why does using a variable change the output in xsl?

I am attempting to understand how an answer to a previous question of mine works. Here is the old question: XSL Transform with three source documents to create report

I have two questions. One, why does the use of a variable cause the output to change between the nodes "staff-with-membership" and "staff-with-membership-using-variable" (under Center3). My expectation would be that using a variable would have no effect.

Second, how does the "filtering" work for the variable "staff" - I would expect the code "key('membership-by-staff', @StaffID)" to return either one node or none, but somehow it manages to seemingly return two for "Center2" (Charles Glover and Donald Hill).

Source xml:

<root>
 <Staff>
  <Items>
   <Item StaffName="Charles Glover" StaffCenter="Center2" StaffID="CG1" />
   <Item StaffName="Donald Hill" StaffCenter="Center2" StaffID="DH1" />
   <Item StaffName="Evan Dolan" StaffCenter="Center3" StaffID="ED1" />
   <Item StaffName="Frank Miller" StaffCenter="Center3" StaffID="FM1" />   
  </Items>
 </Staff>
 <Membership>
  <Items>
   <Item MembershipStaff_ID="CG1" />
   <Item MembershipStaff_ID="DH1" />
   <Item MembershipStaff_ID="ED1" />   
  </Items>
 </Membership>
</root>

Xsl:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="staff-by-center" match="Staff/Items/Item" use="@StaffCenter"/>
<xsl:key name="membership-by-staff" match="Membership/Items/Item" use="@MembershipStaff_ID"/>

<xsl:template match="/root">
    <root>
        <!-- for each distinct center -->
        <xsl:for-each select="Staff/Items/Item[count(.|key('staff-by-center', @StaffCenter)[1]) = 1]">

            <xsl:variable name="center" select="@StaffCenter" />

            <!-- staff at current center -->
            <xsl:variable name="all-staff" select="key('staff-by-center', $center)" />

            <!-- exclude staff with no memberships -->
            <xsl:variable name="staff" select="$all-staff[key('membership-by-staff', @StaffID)]" />

            <!-- staff using variable for StaffID instead of attribute -->
            <xsl:variable name="myStaffId" select="@StaffID" />
            <xsl:variable name="staff-using-variable" select="$all-staff[key('membership-by-staff', $myStaffId)]" />

            <center>
                <name>
                    <xsl:value-of select="$center" />
                </name>
                <all-staff-in-center>
                    <xsl:copy-of select="$all-staff" />
                </all-staff-in-center>
                <staff-with-membership>
                    <xsl:copy-of select="$staff" />
                </staff-with-membership>
                <membership>
                    <xsl:copy-of select="key('membership-by-staff',  @StaffID)" />
                </membership>
                <staff-with-membership-using-variable>
                    <xsl:copy-of select="$staff-using-variable" />
                </staff-with-membership-using-variable>
            </center>
        </xsl:for-each>
    </root>
</xsl:template>
</xsl:stylesheet>

Output xml:

<root>
   <center>
      <name>Center2</name>
      <all-staff-in-center>
         <Item StaffName="Charles Glover" StaffCenter="Center2" StaffID="CG1"/>
         <Item StaffName="Donald Hill" StaffCenter="Center2" StaffID="DH1"/>
      </all-staff-in-center>
      <staff-with-membership>
         <Item StaffName="Charles Glover" StaffCenter="Center2" StaffID="CG1"/>
         <Item StaffName="Donald Hill" StaffCenter="Center2" StaffID="DH1"/>
      </staff-with-membership>
      <membership>
         <Item MembershipStaff_ID="CG1"/>
      </membership>
      <staff-with-membership-using-variable>
         <Item StaffName="Charles Glover" StaffCenter="Center2" StaffID="CG1"/>
         <Item StaffName="Donald Hill" StaffCenter="Center2" StaffID="DH1"/>
      </staff-with-membership-using-variable>
   </center>
   <center>
      <name>Center3</name>
      <all-staff-in-center>
         <Item StaffName="Evan Dolan" StaffCenter="Center3" StaffID="ED1"/>
         <Item StaffName="Frank Miller" StaffCenter="Center3" StaffID="FM1"/>
      </all-staff-in-center>
      <staff-with-membership>
         <Item StaffName="Evan Dolan" StaffCenter="Center3" StaffID="ED1"/>
      </staff-with-membership>
      <membership>
         <Item MembershipStaff_ID="ED1"/>
      </membership>
      <staff-with-membership-using-variable>
         <Item StaffName="Evan Dolan" StaffCenter="Center3" StaffID="ED1"/>
         <Item StaffName="Frank Miller" StaffCenter="Center3" StaffID="FM1"/>
      </staff-with-membership-using-variable>
   </center>
</root>

Upvotes: 2

Views: 51

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167506

With

        <xsl:variable name="myStaffId" select="@StaffID" />
        <xsl:variable name="staff-using-variable" select="$all-staff[key('membership-by-staff', $myStaffId)]" />

you select the @StaffID attribute for the context node (of the outer for-each) only.

In the expression <xsl:variable name="staff" select="$all-staff[key('membership-by-staff', @StaffID)]" /> the attribute @StaffID is selected for each node in $all-staff as the expression is inside the predicate in square brackets. You would need to use $all-staff[key('membership-by-staff', current()/@StaffID)] to have the same result as with your use of the variable where you only select the StaffID of the context node.

As for $all-staff being filtered by the predicate [key('membership-by-staff', @StaffID)], the predicate expression is evaluated for each node in $all-staff and tests whether there is a reference in Membership/Items/Item. There is for both the persons of Center2 but only for one of Center3.

Upvotes: 2

Related Questions