0

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 Answers3

2

In 2.0 I think this should work:

/root/resolve-QName(@name, .) = xs:QName('aTest:a')
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • I had found resolve-QName already. It's exactly what I want, but wasn't recognised in my environment. The version of XSLT/XPath now explains this! Yes, I'm limited to 1.0. – Christopher Kellett Feb 12 '23 at 12:42
1

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)

Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31
  • Thanks. I feared it might end up with something this ugly, but at least I know I'm not missing something easy. I'll try making this into a generic template I can reuse for multiple similar tests. – Christopher Kellett Feb 12 '23 at 12:53
  • Follow up question: `document('')` produces the warning `Execution of the 'document()' function was prohibited. Use the XsltSettings.EnableDocumentFunction property to enable it.` in VS 2022. If I understand correctly, such a setting is part of the .NET invocation of the XSLT file, and can be set when calling the transformation. However, I have not yet written the C# handling of the XML at all, so can't set the setting. Is there a place to set it for the debugger, or have I misunderstood and cannot in fact use `document('')` here? – Christopher Kellett Feb 12 '23 at 14:43
  • See my augmented answer for a suggestion. – Heiko Theißen Feb 12 '23 at 15:07
0

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.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110