在下面的示例代码中,任何形式为“// elementName ”的XPath都会在源xml具有名称空间前缀时返回null(请参阅底部代码中的testWithNS()
)。
当源xml没有名称空间前缀时,所有列出的XPath表达式都返回一个节点(参见testNoNS()
)。
我知道我可以通过设置NamespaceContext(如testWithNSContext()
),将xml解析为名称空间感知文档,并在XPath中使用名称空间前缀来解决这个问题。但是我不想这样做,因为我的实际代码需要使用和不使用名称空间前缀来处理xml。
我的问题是为什么它只是:
返回null,但testWithNS()
中的所有其他示例都返回节点?
输出
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
代码
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();
}
}
}
我现在使用Saxon将XPahtFactory行更改为此
来测试相同的代码final XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl();
使用Saxon testWithNS()
中的所有行返回*** NOT FOUND ***
而不是像' elementName '那样返回默认的Xalan实现。
鉴于我正在使用非命名空间感知文档构建器工厂来解析xml,为什么这些xpath都不起作用,只有一些使用Xalan?
答案 0 :(得分:3)
如果要忽略名称空间,可以使用local-name
XPath函数:
//*[local-name()='grandchild1']
答案 1 :(得分:2)
鉴于我正在使用非命名空间感知文档构建器工厂来解析xml,为什么这些xpath都不起作用,只有一些使用Xalan?
XPath语言仅在名称空间良好的XML文档和片段上定义。如果您在没有命名空间支持的情况下进行解析,那么所有的赌注都会关闭,无法保证任何XPath表达式都能在由非命名空间感知的解析器创建的DOM上正常工作(即使相关文档不使用任何命名空间)
我知道在为它提供非NS感知文档时,我已经看到Java内置XSLT处理器出现了非常不一致的行为。