Paul - The Programmer

simple & stupid

XPath error: Cannot select a node here: the context item is an atomic value

Assume the input xml file is like

<people>
    <person>
        <name>paul</name>
        <gender>male</gender>
    </person>
    <person>
        <name>lee</name>
        <gender>female</gender>
    </person>
</people>

This XPath error complained for the below style sheet. 

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:element name="team">
                <xsl:for-each select="distinct-values(//gender)">
                    <xsl:variable name="gender" select="."/>
                    <xsl:element name="{$gender}">
                        <xsl:for-each select="//person[gender=$gender]"> <!-- Wrong! A node is selected for actomic context item. -->
                            <xsl:variable name="name" select="name"/>
                            <xsl:element name="{$name}"/> 
                        </xsl:for-each>
                    </xsl:element>
                </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

This error means that a node was selected for a actomic context item. The atomic context item is chosen by the outer for-each loop <xsl:for-each select="distinct-values(//gender)"/>.

Node items can not be selected in this loop context.

The solution is define a variable outside of loop. the variable value is the string of the node path. Then select the node with that variable.

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:element name="team">
            <xsl:variable name="person" select="//person"/> <!-- Define the variable with the node path. -->
                <xsl:for-each select="distinct-values(//gender)">
                    <xsl:variable name="gender" select="."/>
                    <xsl:element name="{$gender}">
                        <xsl:for-each select="$person[gender=$gender]"> <!-- Select the node by the variable defined outside of this for-each loop. -->
                            <xsl:variable name="name" select="name"/>
                            <xsl:element name="{$name}"/> 
                        </xsl:for-each>
                    </xsl:element>
                </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Weird enough, but seems like the only available solution.