...
Turns out that there are some "issues" with XPath 1.0 and XML namespaces.
...
No XPath way to bind prefixes
If you have XML data that has a namespace, such as:
...
There is no XPath 1.0-standard mechanism for associating a namespace with a prefix (or with the default namespace). By that I mean there is nothing you can put in the path expression itself to specify the namespaces. Such mechanisms are available on APIs specific to the XPath-1.0 processor.
Things that work
There are a few ways to deal with this problem.
...
.
...
XPath-1.0 processors typically provide a way to bind namespace external to the XPath 1.0 expression. For example, in JAXB, namespaces are bound to prefixes (and to the default namespace) using the NamespaceContext method.
See XPath.html and the discussion on QNames in the class overview. Also see NamespaceContext.html for details on how to bind the default namespace.
1) Avoid Namespaces
If you have all non-qualified elements with none of these xmlns="..." things around like this:
<data><a>75</a></data>
well, this works fine with the XPath /data/a.
2) Prefix All Element Names:
You can qualify every element. This is in some sense the most robust approach. So if you write:
<tns:data xmlns:tns="urn:someNamespaceOrOther"><tns:a>75</tns:a></tns:data>
Then the path
/tns:data/tns:a
is meaningful and works. However, for an XPath-1.0 processor, the tns prefix will need to be bound before the XPath expression will evaluate successfully. See option (0) above for details. (Note that this path will work without any hassle in an XPath-2.0 processor.)
3) Use Both Non-Qualified Names and a Prefix
If you really can't stand the tns prefix clutter in the data, then you can add a prefix along side the non-prefix namespace. This prefix is there for XPath:
<data xmlns="urn:someNamespaceOrOther" xmlns:tns="urn:someNamespaceOrOther"><a>75</a></data>
This tns prefix definition enables qualified XPath expressions to also work. Notice that the unqualified namespace and the tns namespace prefix are bound to the same namespace, so this path:
/tns:data/tns:a
also works in this case. Again, see note above on binding namespaces.
4) Match the namespace and/or the local-name of the element in the XPath expression
For the original example, this XPath 1.0 expression should work:
/*[local-name() = 'data' and namespace() = 'urn:someNamespaceOrOther']/*[local-name() = 'a' and namespace() = 'urn:someNamespaceOrOther']
Summary
Daffodil uses Saxon-B and constructs a namespace context object to provide resolution of prefixes for XPath.
The way this works, is that whenever we have a DFDL expression, we also have the encapsulating XML object that contained it. The namespace scope of that XML object defines what the prefixes in the expression mean. So we grab the namespace scope from the XML object, and massage it slightly to what Saxon-B wants to provide those definitions.
Some Surprises
Managing namespace prefixes can be a little bit tricky.
Suppose you write a schema but use xmlns="http://www.w3.org/2001/XMLSchema".
This allows you to avoid the "xs:" prefix on all the XML Schema elements.
However, it will require all expressions to use an explicit prefix on every path step, since an unprefixed path step will imply the XML Schema namespace. This shouldn't be surprising. All QNames in the schema referring to other elements, types, groups, defineFormat or defineEscapeScheme also must be prefixed in this case. But path expressions it is very easy to remember that one must prefix all the steps as those are QNames as well.
If you really want to write expressions like /a/b/c, i.e., without any prefixes on the steps, then you have to use the default namespace the same as the schema's target namespace.
There are several ways to get namespaces to work with XPath 1.0, but none of them are pretty or elegant.