andrewjwelch.com
schema-aware.com
schema-aware XSLT and XQuery examples
SAXONICA

The data() function

The data() function returns the typed value of its argument
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    

    
<xsl:import-schema>
        
<xs:schema>            
            
<xs:element name="person" type="person" />           
            
            
<xs:complexType name="person">
                
<xs:simpleContent>
                    
<xs:extension base="xs:string">
                        
<xs:attribute name="married" type="xs:boolean" />
                    
</xs:extension>
                
</xs:simpleContent>                
            
</xs:complexType>
        
</xs:schema>
    
</xsl:import-schema>
    
    
<xsl:variable name="input">
        
<person xsl:type="person" married="false">typed singleton</person>
        
<person married="false">singleton</person>
    
</xsl:variable>
    
    
<xsl:template match="/" name="main">
        
<xsl:for-each select="$input/person">
            
<xsl:value-of select="concat('&#xa;',.)" />
            
<xsl:if test="data(@married)"> - married</xsl:if>
        
</xsl:for-each>
    
</xsl:template>   
    
</xsl:stylesheet>
output:
typed singleton
singleton - married				
				
This standalone transform demonstrates the data() function. The $input variable contains two <person> elements, the first one is validated using xsl:type which adds type annotatations to that element and its desecendants, the second is just "plain" XML where the types are xs:untyped and xs:untypedAtomic.
In the root matching template, each <person> element is processed and its string value is output (the concat('&#xa;',.) simply outputs a newline before the string value). The key part is the test on the @married attribute - test="data(@married)" The same test is performed on the same markup, the only difference is that one is type annotated and the other is not.
The first call to data(@married) returns a boolean, because the attribute is defined as xs:boolean in the schema and has been annotated with that type during validation. This means "false" is returned as false() - the boolean type.
The second call to data(@married) returns the untyped attribute and the Effective Boolean Value (EBV) rules are applied. For nodes, the EBV is true if the node exists, so because there is a @married attribute the test evaluates to true.
Typically though, you're likely to always do xs:boolean(@married) to convert the string value of the node to a boolean.
References:
Construction from a PSVI (typed-value heading)