How should I resolve and compare two QNames using XPath for XSLT?

122 views Asked by At

I want to test, within an XSLT document, whether an attribute value of type QName from the input XML is equal to another QName from the XSLT document. I want to evaluate each QName according to its parent document namespace declarations before the comparison.

So, the following XSLT...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="msxsl"
    xmlns:aTest="http://tempuri.org/Test"
>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:if test="/@name = 'aTest:a'">Success</xsl:if>
    </xsl:template>
</xsl:stylesheet>

...given the following input...

<?xml version="1.0" encoding="utf-8" ?>
<root
    xmlns:test="http://tempuri.org/Test"
    name="test:a"
></root>

...would produce "Success" (I know why it doesn't as is, but hopefully you see my intention).

3

There are 3 answers

3
Heiko Theißen On BEST ANSWER

You must compare the parts before and after the : separately. The namespace prefix before the : can be translated into the namespace URI by looking it up on the corresponding namespace axis:

  • For the source document node this is namespace::*.
  • For the stylesheet this is document('')/*/namespace::*.
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:aTest="http://tempuri.org/Test">
  <xsl:template match="root">
    <xsl:variable name="v">aTest:a</xsl:variable>
    <xsl:if test="namespace::*[local-name()=substring-before(current()/@name,':')] =
                  document('')/*/namespace::*[local-name()=substring-before($v,':')]
              and substring-after(@name,':') = substring-after($v,':')">Success</xsl:if>
  </xsl:template>
</xsl:stylesheet>

If, for some reason, you cannot invoke the function document(''), you can simply write the namespace URI and the local name in the condition:

namespace::*[local-name()=substring-before(current()/@name,':')] =
'http://tempuri.org/Test'
and substring-after(@name,':') = 'a'

(XSLT 1.0)

0
Martin Honnen On

XPath 2.0 and later (I think) has resolve-QName (https://www.w3.org/TR/xpath-functions/#func-resolve-QName) e.g. <xsl:if test="node-name(@name) = resolve-QName('aTest:a', document('')/*)">Success</xsl:if> might make some sense in XSLT 2 and later.

1
Michael Kay On

In 2.0 I think this should work:

/root/resolve-QName(@name, .) = xs:QName('aTest:a')