BUG: Position() and Last() XPath Functions Are Ignored When You Use Them as XSLT Function Arguments (324033)



The information in this article applies to:

  • Microsoft XML Classes (included with the .NET Framework 1.0)

This article was previously published under Q324033

SYMPTOMS

The position() and last() XPath functions are ignored when you use them directly as XSLT function (standard, script, and extension function) arguments in XPath query expressions that are used in XSLT style sheets.

CAUSE

The XPath processor executes the XSLT functions before it sets the context of the position() and last() XPath functions.

RESOLUTION

Use XSLT variables to store the values that these functions return. You can then use the XSLT variables as the XSLT function arguments in XPath query expressions that are used in the style sheet.

STATUS

Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article.

MORE INFORMATION

Note that this problem does not occur when the position() and last() functions are used in a predicate in an expression that is supplied as an XSLT function argument.

Steps to Reproduce the Behavior

  1. In Microsoft Visual Studio .NET, create a new Visual Basic .NET Console Application project.
  2. Use the following code to create and add an XML document named Repro.xml to the project:
    <Data>
    	<value>1</value>
    	<value>2</value>
    	<value>3</value>
    	<value>78</value>
    	<value>5</value>
    	<value>88</value>
    	<value>7</value>
    </Data>
    					
  3. Use the following code to create and add an XSLT style sheet named Repro.xsl to the project. Study the inline comments to understand the functionality of the code in the style sheet:
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:key name="Values" match="value" use="."/>
    
        <xsl:template match="Data">
    	<Data>
    	        
    	<!-- 
    
    	Use the XSLT key() function to identify and output 
    	value elements whose value matches their position in the source XML. 
    
    	The following will not work because the position() function is supplied 
    	directly as an argument of the key() function. 
    
    	--> 
    	<ValuesThatMatchPosition>        
    		<xsl:for-each select="value">
           		    <xsl:if test="key('Values',position())">
    		            <value><xsl:value-of select="."/></value>   		    
    		   </xsl:if>	   
    		</xsl:for-each>	
        	</ValuesThatMatchPosition>
    
    	<!-- 
    
    	The position() function works correctly when used in a predicate in an expression supplied
    	as an XSLT function (the format-number function is used in this sample) argument. 
    
    	The select expression in the following line will retrieve the value of the 3rd value element 
    	in the source XML.
    
    	--> 
    	
    	<Position3Value><xsl:value-of select="format-number(value[position()=3],'0')"/></Position3Value>	
            
    	</Data>
        </xsl:template>       
    
    </xsl:stylesheet> 
    					
  4. Paste the following code in the Sub Main() procedure in Module1.vb to execute the XSLT transformation by applying Repro.xsl to Repro.xml:
     Dim transform As New System.Xml.Xsl.XslTransform()
     transform.Load("..\Repro.xsl")
     transform.Transform("..\Repro.xml", "..\output.xml")
     Console.WriteLine("Done")
     Dim response As String = Console.ReadLine
    					
  5. Save and run the project. The code in the Sub Main() procedure executes the XSLT transformation and writes the transformation output to a file named Output.xml in the project folder. The message "Done" appears in the console window when the transformation completes. Press any key to close the console window and return to the development environment.
  6. In Solution Explorer, click Refresh button to view the Output.xml file in the project folder. When you open the Output.xml file, you will see that it contains the following XML:
    <?xml version="1.0" encoding="utf-8"?>
    <Data>
    	<ValuesThatMatchPosition></ValuesThatMatchPosition>
    	<Position3Value>3</Position3Value>
    </Data>
    					
    The value elements in the source XML whose values match their position are not listed in the output as expected. The <Position3Element> element, however, has the correct value because the expression that is used in the style sheet to retrieve it uses the position function in a predicate (instead of directly as an XSLT function argument).

  7. Close Output.xml, and then open the Repro.xsl style sheet.
  8. Replace the <xsl:for-each> block that is used to generate the contents of the <ValuesThatMatchPosition> element with the following code to store the output of the position XPath function in an XSLT variable and supply the variable as the value parameter in the call to the key XSLT function:
    <xsl:for-each select="value">
       <xsl:variable name="pos" select="position()"/>			
       <xsl:if test="key('Values',$pos)">
                <value><xsl:value-of select="."/></value>   		    
       </xsl:if>	   
    </xsl:for-each>
    					
  9. Save the changes to the style sheet, and then reexecute the project. When you view the contents of Output.xml, you see the following XML:
    <?xml version="1.0" encoding="utf-8"?>
    <Data>
    	<ValuesThatMatchPosition>
    		<value>1</value>
    		<value>2</value>
    		<value>3</value>
    		<value>5</value>
    		<value>7</value>
    	</ValuesThatMatchPosition>
    	<Position3Value>3</Position3Value>
    </Data>
    					
    The output now lists the value elements in the source XML whose values match their position.

Modification Type:MinorLast Reviewed:7/16/2004
Keywords:kbbug kbpending KB324033