Why only certain XPath expressions find nodes when xml has a namespace prefix

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?

+6
source share
2 answers

If you want to ignore namespaces, you can use the XPath local-name function:

 //*[local-name()='grandchild1'] 
+1
source

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?

XPath is defined only on well-formed XML documents and namespace fragments. If you analyze without namespace support, then all bets are disabled, there is no guarantee that any XPath expression will work correctly in a DOM created by a parser that does not support a namespace (even if this document does not use a namespace).

I know that I saw wildly inconsistent behavior from the embedded XSLT Java processor when providing it with non-NS documents.

+1
source

Source: https://habr.com/ru/post/950189/


All Articles