When we load an XML file, if it uses namespaces we have to pass all those namespaces to the compiler.
First off, is there some call that tells the compiler to read the XML file itself and use the prefixes/URIs declared in the file? It's always struck me as weird that I have to tell the compiler something that it can figure out itself.
But if this does need to be done, is the following the best way to do it?
The full code for this is in SaxonQuestions.zip (minus license key) - XmlDatasource.java.
public class XmlDatasource {
/** the DOM all searches are against */
private XdmNode xmlRootNode;
private XPathCompiler xPathCompiler;
/** key == the prefix; value == the uri mapped to that prefix */
private HashMap<String, String> prefixToUriMap = new HashMap<>();
/** key == the uri mapped to that prefix; value == the prefix */
private HashMap<String, String> uriToPrefixMap = new HashMap<>();
// .................
private void declareNameSpaces() throws SaxonApiException {
// saxon has some of their functions set up with this.
prefixToUriMap.put("saxon", "http://saxon.sf.net");
uriToPrefixMap.put("http://saxon.sf.net", "saxon");
XdmValue list = xPathCompiler.evaluate("//namespace::*", xmlRootNode);
if (list == null || list.size() == 0)
return;
for (int index=0; index<list.size(); index++) {
XdmNode node = (XdmNode) list.itemAt(index);
String prefix = node.getNodeName() == null ? "" : node.getNodeName().getLocalName();
// xml, xsd, & xsi are XML structure ones, not ones used in the XML
if (prefix.equals("xml") || prefix.equals("xsd") || prefix.equals("xsi"))
continue;
// use default prefix if prefix is empty.
if (prefix == null || prefix.isEmpty())
prefix = "def";
// this returns repeats, so if a repeat, go on to next.
if (prefixToUriMap.containsKey(prefix))
continue;
String uri = node.getStringValue();
if (uri != null && !uri.isEmpty()) {
xPathCompiler.declareNamespace(prefix, uri);
prefixToUriMap.put(prefix, uri);
uriToPrefixMap.put(uri, prefix); }
}
}
}
In general, declaring the namespaces present in the source document isn't recommended practice, because (a) different bindings can be in scope in different parts of the source document (this is most likely for the default namespace, admittedly), and (b) you want the XPath expression to retain its meaning if the document author chooses to use different prefixes. So I wouldn't want to "bless" than practice by automating it.
An alternative to your approach (
//namespace::*
) would be to do the navigation from Java: something like(This assumes static imports for
Steps.*
andPredicates.*
)I don't know whether there's any benefit in eliminating duplicates in your scanning code, or just letting
XPathCompiler.declareNamespace()
do it.