I have the following XML file:
<foo:a xmlns:foo=\"http://www.foo.com\">
<foo:b foo:bar=\"zar\">
</foo:b>
</foo:a>
To get the b nodes with an attribute bar having the value "zar" (all in the correct namespace), I can use the XPath expression:
/foo:a/foo:b[@foo:bar=\"zar\"]
(with "foo" properly bound to "http://www.foo.com" - see code at the end)
However, when I want to do the same in a namespace-unaware way, though I can remove the namespace from the elements by relying on the local-name()
function, I am not able to remove them from the attribute.
This is the best I can come up with:
/*[local-name()='a']/*[local-name()='b' and @foo:bar=\"zar\"]
(where foo
is, regrettably, used to qualify the bar
attribute).
How can I write the above expression with namespaces completely removed?
Below code has been tested both with and without Saxon-HE-9.4.jar on the CLASSPATH and produces correct results, but I can't get the 'foo' namespace prefix out of the second XPath expression!
import java.io.*;
import java.util.*;
import javax.xml.xpath.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.namespace.NamespaceContext;
public class FooMain {
public static void main(String args[]) throws Exception {
String xmlSample = "<foo:a xmlns:foo=\"http://www.foo.com\"><foo:b foo:bar=\"zar\"></foo:b></foo:a>";
XPath xpath = namespaceAwareXpath("foo", "http://www.foo.com");
{
System.out.printf("[NS-aware ] Number of 'b' nodes with foo:bar attribute having the value \"zar\" is: %d\n",
((NodeList) xpath.compile("/foo:a/foo:b[@foo:bar=\"zar\"]").evaluate(stringToXML(xmlSample, true),
XPathConstants.NODESET)).getLength());
}
{
System.out.printf("[NS-aware but using local-name() ] Number of 'b' nodes with foo:bar attribute having the value \"zar\" is: %d\n",
((NodeList) xpath.compile("/*[local-name()='a']/*[local-name()='b' and @foo:bar=\"zar\"]").evaluate(stringToXML(xmlSample, true),
XPathConstants.NODESET)).getLength());
}
}
public static XPath namespaceAwareXpath(final String prefix, final String nsURI) {
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
NamespaceContext ctx = new NamespaceContext() {
@Override
public String getNamespaceURI(String aPrefix) {
if (aPrefix.equals(prefix))
return nsURI;
else
return null;
}
@Override
public Iterator getPrefixes(String val) {
throw new UnsupportedOperationException();
}
@Override
public String getPrefix(String uri) {
throw new UnsupportedOperationException();
}
};
xpath.setNamespaceContext(ctx);
return xpath;
}
private static Document stringToXML(String s, boolean nsAware) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(nsAware);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(s.getBytes("UTF-8")));
}
}
For this purpose, an XPath predicate is treated as a combination of positional and value filters on XML elements and attributes appearing in an XML document. For example, the following XPath expression can be deciphered as a set of checks on the XML document. The list following the example explains those checks.
1) By Using a Prefix You can easily avoid the XML namespace by using a name prefix.
The local-name function returns a string representing the local name of the first node in a given node-set.
Introduction to XPath namespace. In an XML document, namespaces are used to provide uniquely named components and attributes. A namespace is made up of two parts: a prefix and a URL. This indicates the location of a document that defines the namespace in question.
/*[local-name()='a']/*[local-name()='b' and @*[local-name() = 'bar']=\"zar\"]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With