Search code examples
xsltxpathnodename

node-name not returning expanded QName in XSLT


This code is supposed to print the full path to all elements and attributes in the data.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="yes">

   <xsl:output method="text"/>
   <xsl:template match="text()"/>

   <xsl:template match="*">
       {ancestor-or-self::*/concat(node-name(),'/')}<xsl:apply-templates select="@*|*"/>
   </xsl:template>
   <xsl:template match="@*">{'@' || node-name() || ','}</xsl:template>

 </xsl:stylesheet>

So with this input

<a:b xmlns:a="ans" xmlns:c="cns">
    <c:x/>
</a:b>

I expect

{ans}b
{ans}b/ {cns}x

precise spacing is immaterial.

I am getting output as if I had used the name() function instead of node-name i.e

a:b
a:b / c:x

There is probably a work-around by concatenating local-name and namespace-uri but I would like to know why what is posted isn't doing what I hoped it would.


Solution

  • Perhaps the path function helps: <xsl:value-of select="descendant::*!path()" separator="&#10;"/>.

    Or construct the format you want from namespace-uri-from-QName and the local-name-fromQName:

    <xsl:value-of select="descendant::*!string-join(ancestor-or-self::*!node-name()!('{' || namespace-uri-from-QName(.) || '}' || local-name-from-QName(.)), '/')" separator="&#10;"/>
    

    https://xsltfiddle.liberty-development.net/naZXVFj

    As for why node-name() returns an xs:QName but its string value gives the format you see, I think https://www.w3.org/TR/xpath-functions-31/#casting-to-string specifies about casting an xs:QName to a string:

    If ST is xs:QName or xs:NOTATION:

    if the qualified name has a prefix, then TV is the concatenation of the prefix of SV, a single colon (:), and the local name of SV.

    otherwise TV is the local-name.