Search code examples
xmlxpathxpath-2.0

requiring the value of `xsi:type` to be a type in a specific namespace regardless of prefix


Say I have the following XML file:

<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:foo="http://foo"
   xsi:type="foo:SomeSubtypeOfA">
    boo
</A>

I wish to require that the XML instance is of a specific type derived from A, e.g. type {http://foo}SomeSubtypeOfA (as indeed the above example is). So I am using the following XPath:

/*[@xsi:type='foo:SomeSubtypeOfA']  

... and check whether it succeeds in returning a node or not. However the XPath above is problematic as it relies on the arbitrary prefix so it fails on the following file:

<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:foo2="http://foo"
   xsi:type="foo2:SomeSubtypeOfA">
    boo
</A>

... and it produces a false match on the following file:

<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:foo="http://fooX"
   xsi:type="foo:SomeSubtypeOfA">
    boo
</A>

To get rid of the false match I could write the XPath as:

/*[namespace::foo[.='http://foo'] and @xsi:type='foo:SomeSubtypeOfA'] 

But that still fails to account for the possibility of some other prefix bound to the http://foo namespace. How can I write the above XPath so it checks the namespace of the type given in the xsi:type attribute rather than its prefix?


Solution

  • In XPath 2.0 you can do

    /*[namespace-uri-from-QName(resolve-QName(@xsi:type, .)) = 'http://foo' and local-name-from-QName(resolve-QName(@xsi:type, .)) = 'SomeSubtypeOfA']
    

    In XPath 1.0 you need to do something like

    //*[namespace::*[name()=substring-before(../@xsi:type, ':')] = 'http://foo/']
    

    (but that probably doesn't allow for the type being in the default namespace)