我有以下示例XML文件:
<a xmlns="http://www.foo.com">
<b>
</b>
</a>
使用XPath
表达式/foo:a/foo:b
(在'foo'
中正确配置NamespaceContext
)我可以正确计算b
个节点的数量,代码可以正常工作当Saxon-HE-9.4.jar
在CLASSPATH上时以及它不在时。
但是,当我使用命名空间 - unaware DocumentBuilderFactory
解析同一文件时,XPath表达式“/ a / b”正确计算b
个节点的数量仅当CLASSPATH上的Saxon-HE-9.4.jar
不时。
以下代码:
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 = "<a xmlns=\"http://www.foo.com\"><b></b></a>";
{
XPath xpath = namespaceUnawareXpath();
System.out.printf("[NS-unaware] Number of 'b' nodes is: %d\n",
((NodeList) xpath.compile("/a/b").evaluate(stringToXML(xmlSample, false),
XPathConstants.NODESET)).getLength());
}
{
XPath xpath = namespaceAwareXpath("foo", "http://www.foo.com");
System.out.printf("[NS-aware ] Number of 'b' nodes is: %d\n",
((NodeList) xpath.compile("/foo:a/foo:b").evaluate(stringToXML(xmlSample, true),
XPathConstants.NODESET)).getLength());
}
}
public static XPath namespaceUnawareXpath() {
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
return xpath;
}
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")));
}
}
运行上述内容:
java -classpath dist/foo.jar FooMain
..产生:
[NS-unaware] Number of 'b' nodes is: 1
[NS-aware ] Number of 'b' nodes is: 1
以:
运行java -classpath Saxon-HE-9.4.jar:dist/foo.jar FooMain
...生产:
[NS-unaware] Number of 'b' nodes is: 0
[NS-aware ] Number of 'b' nodes is: 1
答案 0 :(得分:4)
正确观察。 Saxon不支持名称空间不知道的DOM。它没有理由应该这样做。如果你能找到一个与命名空间无关的DOM一起使用的XSLT / XPath处理器,那么请继续使用它,如果你愿意,但它的行为不是由任何标准定义的。
如果Saxon有可能检测到DOM是名称空间不知道的,那么它会抛出错误而不是给出虚假结果。可悲的是,DOM的许多设计失败之一是,如果你自己没有创建DOM,你就无法判断它是否是名称空间感知。
您的评论“我需要对命名空间保持宽容,因为我必须处理并非总是XSD有效的第三方XML实例。”是一个完整的非sequitur。确实,文档不能是XSD有效的,除非它是名称空间有效的,但相反的情况并非如此;大量文档是名称空间有效的,而不是XSD有效。
最后,正如您的经验所示,依赖于JAXP机制来加载恰好位于类路径上的任何XPath处理器非常容易出错。您无法控制是否通过此机制获得XPath 1.0或2.0处理器(再次,您无法轻易找到您所拥有的)。如果您的代码依赖于特定XPath实现的怪癖,那么您需要显式加载该实现,而不是依赖于JAXP搜索。
UPDATE(2015年9月):Saxon 9.6不再包含将其作为JAXP XPath提供程序公布的meta-inf服务文件。这意味着你永远不会将Saxon作为你的XPath处理器,因为它在类路径上:你必须明确地要求它。
答案 1 :(得分:1)
XPath语言仅在名称空间良好的XML上定义,因此不同的处理器在非命名空间感知的DOM树(甚至像<a><b/></a>
之类的行为上的行为,如果在命名空间中解析了它 - 意识到的方式,实际上不会使用任何命名空间)最好是特定于实现,最坏的是完全未定义。
答案 2 :(得分:1)
Saxon 10现在支持不带名称空间的XPath,您可以这样配置它:
XPath xPath = new net.sf.saxon.xpath.XPathFactoryImpl().newXPath();
((XPathEvaluator)xPath).getStaticContext().setUnprefixedElementMatchingPolicy(UnprefixedElementMatchingPolicy.ANY_NAMESPACE);