In the code example below, any XPath that is in the form "// elementName" returns null when the source xml has a namespace prefix (see testWithNS() in the code below).
If the source xml does not have a namespace prefix, all of the XPath expressions listed will return a node (see testNoNS() ).
I know that I can solve this problem by setting the NamespaceContext (as in testWithNSContext() ), testWithNSContext() xml as a document supporting the namespace, and using namespace prefixes in XPaths. However, I do not want to do this, since my actual code must process xml with and without namespace prefixes.
My question is why is this only:
- //test
- // child1
- // grandchild1
- // child2
which return null but all other examples in testWithNS() return node?
Output
testNoNS() test = found /test = found //test = found //test/* = found //test/child1 = found //test/child1/grandchild1 = found //test/child2 = found //child1 = found //grandchild1 = found //child1/grandchild1 = found //child2 = found testWithNS() test = found /test = found //test = *** NOT FOUND *** //test/* = found //test/child1 = found //test/child1/grandchild1 = found //test/child2 = found //child1 = *** NOT FOUND *** //grandchild1 = *** NOT FOUND *** //child1/grandchild1 = found //child2 = *** NOT FOUND *** testWithNSContext() ns1:test = found /ns1:test = found //ns1:test = found //ns1:test/* = found //ns1:test/ns1:child1 = found //ns1:test/ns1:child1/ns1:grandchild1 = found //ns1:test/ns1:child2 = found //ns1:child1 = found //ns1:grandchild1 = found //ns1:child1/ns1:grandchild1 = found //ns1:child2 = found
code
import java.io.StringReader; import java.util.Iterator; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.junit.Test; import org.w3c.dom.Document; import org.xml.sax.InputSource; public class XPathBugTest { private String xmlDec = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"; private String xml = xmlDec + "<test>" + " <child1>" + " <grandchild1/>" + " </child1>" + " <child2/>" + "</test>"; private String xmlNs = xmlDec + "<ns1:test xmlns:ns1=\"http://www.wfmc.org/2002/XPDL1.0\">" + " <ns1:child1>" + " <ns1:grandchild1/>" + " </ns1:child1>" + " <ns1:child2/>" + "</ns1:test>"; final XPathFactory xpathFactory = XPathFactory.newInstance(); final XPath xpath = xpathFactory.newXPath(); @Test public void testNoNS() throws Exception { System.out.println("\ntestNoNS()"); final Document doc = getDocument(xml); isFound("test", xpath.evaluate("test", doc, XPathConstants.NODE)); isFound("/test", xpath.evaluate("/test", doc, XPathConstants.NODE)); isFound("//test", xpath.evaluate("//test", doc, XPathConstants.NODE)); isFound("//test/*", xpath.evaluate("//test/*", doc, XPathConstants.NODE)); isFound("//test/child1", xpath.evaluate("//test/child1", doc, XPathConstants.NODE)); isFound("//test/child1/grandchild1", xpath.evaluate("//test/child1/grandchild1", doc, XPathConstants.NODE)); isFound("//test/child2", xpath.evaluate("//test/child2", doc, XPathConstants.NODE)); isFound("//child1", xpath.evaluate("//child1", doc, XPathConstants.NODE)); isFound("//grandchild1", xpath.evaluate("//grandchild1", doc, XPathConstants.NODE)); isFound("//child1/grandchild1", xpath.evaluate("//child1/grandchild1", doc, XPathConstants.NODE)); isFound("//child2", xpath.evaluate("//child2", doc, XPathConstants.NODE)); } @Test public void testWithNS() throws Exception { System.out.println("\ntestWithNS()"); final Document doc = getDocument(xmlNs); isFound("test", xpath.evaluate("test", doc, XPathConstants.NODE)); isFound("/test", xpath.evaluate("/test", doc, XPathConstants.NODE)); isFound("//test", xpath.evaluate("//test", doc, XPathConstants.NODE)); isFound("//test/*", xpath.evaluate("//test/*", doc, XPathConstants.NODE)); isFound("//test/child1", xpath.evaluate("//test/child1", doc, XPathConstants.NODE)); isFound("//test/child1/grandchild1", xpath.evaluate("//test/child1/grandchild1", doc, XPathConstants.NODE)); isFound("//test/child2", xpath.evaluate("//test/child2", doc, XPathConstants.NODE)); isFound("//child1", xpath.evaluate("//child1", doc, XPathConstants.NODE)); isFound("//grandchild1", xpath.evaluate("//grandchild1", doc, XPathConstants.NODE)); isFound("//child1/grandchild1", xpath.evaluate("//child1/grandchild1", doc, XPathConstants.NODE)); isFound("//child2", xpath.evaluate("//child2", doc, XPathConstants.NODE)); } @Test public void testWithNSContext() throws Exception { System.out.println("\ntestWithNSContext()"); final Document doc = getDocumentNS(xmlNs); xpath.setNamespaceContext(new MyNamespaceContext()); isFound("ns1:test", xpath.evaluate("ns1:test", doc, XPathConstants.NODE)); isFound("/ns1:test", xpath.evaluate("/ns1:test", doc, XPathConstants.NODE)); isFound("//ns1:test", xpath.evaluate("//ns1:test", doc, XPathConstants.NODE)); isFound("//ns1:test/*", xpath.evaluate("//ns1:test/*", doc, XPathConstants.NODE)); isFound("//ns1:test/ns1:child1", xpath.evaluate("//ns1:test/ns1:child1", doc, XPathConstants.NODE)); isFound("//ns1:test/ns1:child1/ns1:grandchild1", xpath.evaluate("//ns1:test/ns1:child1/ns1:grandchild1", doc, XPathConstants.NODE)); isFound("//ns1:test/ns1:child2", xpath.evaluate("//ns1:test/ns1:child2", doc, XPathConstants.NODE)); isFound("//ns1:child1", xpath.evaluate("//ns1:child1", doc, XPathConstants.NODE)); isFound("//ns1:grandchild1", xpath.evaluate("//ns1:grandchild1", doc, XPathConstants.NODE)); isFound("//ns1:child1/ns1:grandchild1", xpath.evaluate("//ns1:child1/ns1:grandchild1", doc, XPathConstants.NODE)); isFound("//ns1:child2", xpath.evaluate("//ns1:child2", doc, XPathConstants.NODE)); } private void isFound(String xpath, Object object) { System.out.println(xpath + " = " + (object == null ? "*** NOT FOUND ***" : "found")); } private Document getDocument(final String xml) throws Exception { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); } private Document getDocumentNS(final String xml) throws Exception { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); } public class MyNamespaceContext implements NamespaceContext { @Override public String getNamespaceURI(String prefix) { if ("ns1".equals(prefix)) { return "http://www.wfmc.org/2002/XPDL1.0"; } return XMLConstants.NULL_NS_URI; } @Override public String getPrefix(String uri) { throw new UnsupportedOperationException(); } @Override public Iterator getPrefixes(String uri) { throw new UnsupportedOperationException(); } } }
Update after Saxon test
Now I tested the same code using Saxon, changing the XPahtFactory line to this
final XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl();
With Saxon, all lines in testWithNS() return *** NOT FOUND *** , and not just those that look like // elementName, as with the default Xalan implementation.
Given that I am using the factory nameless document constructor for parsing xml, why none of these xpaths work, and only some of them with Xalan?