如果 Saxon 在 CLASSPATH 上,则无法识别命名空间的 XPath 表达式失败
Posted
技术标签:
【中文标题】如果 Saxon 在 CLASSPATH 上,则无法识别命名空间的 XPath 表达式失败【英文标题】:namespace-unaware XPath expression fails if Saxon is on the CLASSPATH 【发布时间】:2014-02-02 18:41:08 【问题描述】:我有以下示例 XML 文件:
<a xmlns="http://www.foo.com">
<b>
</b>
</a>
使用XPath
表达式/foo:a/foo:b
(在NamespaceContext
中正确配置'foo'
)我可以正确计算b
节点的数量,并且当Saxon-HE-9.4.jar
位于CLASSPATH 和当它不是的时候。
但是,当我使用命名空间解析同一个文件时-不知道 DocumentBuilderFactory
,XPath 表达式“/a/b”仅在 @987654330 时正确计算 b
节点的数量@ 在 CLASSPATH 中不。
代码如下:
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
【问题讨论】:
【参考方案1】:Saxon 10 现在支持没有命名空间的 XPath,您可以像这样配置它:
XPath xPath = new net.sf.saxon.xpath.XPathFactoryImpl().newXPath();
((XPathEvaluator)xPath).getStaticContext().setUnprefixedElementMatchingPolicy(UnprefixedElementMatchingPolicy.ANY_NAMESPACE);
【讨论】:
【参考方案2】:正确的观察。 Saxon 不适用于不知道命名空间的 DOM。没有理由应该这样做。如果你能找到一个 XSLT/XPath 处理器,它可以与不知道命名空间的 DOM 一起工作,那么如果你愿意,可以继续使用它,但它的行为没有任何标准定义。
如果 Saxon 可以检测到 DOM 不知道命名空间,那么它会抛出错误而不是给出虚假结果。可悲的是,DOM 的众多设计失败之一是,如果您没有自己创建 DOM,您将无法判断它是否支持命名空间。
您的评论“我需要对命名空间保持宽容,因为我必须处理并非总是 XSD 有效的第 3 方 XML 实例。”是完全不合逻辑的。确实,除非文档是命名空间有效的,否则文档不能是 XSD 有效的,但反之则不然;大量文档是命名空间有效的,但不是 XSD 有效的。
最后,正如您的经验所示,依靠 JAXP 机制来加载恰好位于类路径中的任何 XPath 处理器非常容易出错。通过这种机制,您甚至无法控制您获得的是 XPath 1.0 还是 2.0 处理器(同样,您无法轻易找出您拥有的是哪个处理器)。如果您的代码依赖于特定 XPath 实现的怪癖,那么您需要显式加载该实现,而不是依赖于 JAXP 搜索。
更新(2015 年 9 月):Saxon 9.6 不再包含将其宣传为 JAXP XPath 提供程序的 meta-inf 服务文件。这意味着您永远不会仅仅因为 Saxon 在类路径上而选择它作为您的 XPath 处理器:您必须明确要求它。
【讨论】:
我使用了对 saxon xpath 引擎的显式调用,但是不知道命名空间的第三方库仍然失败。有什么补救方法吗? 提出一个新问题并提供回答该问题所需的所有详细信息。准确地说出你做了什么以及它是如何失败的。不要尝试将新问题作为 cmets 标记到可能相关或不相关的旧问题上;如果您认为上一个问题是相关的,您可以随时参考。【参考方案3】:XPath 语言仅在命名空间格式良好的 XML 上定义,因此不同处理器在非命名空间感知 DOM 树上的行为(即使是像 <a><b/></a>
这样的,如果它在命名空间感知中解析方式,实际上不会使用任何命名空间)充其量是特定于实现的,最坏的情况是完全未定义。
【讨论】:
我需要对命名空间保持宽容,因为我必须处理并非总是 XSD 有效的第 3 方 XML 实例。我想我必须使用/*[local-name()='a']/*[local-name()='b']
作为我的XPath 表达式。无论撒克逊人是否在场,这似乎都有效。
@MarcusJuniusBrutus 或者如果您知道自己将拥有 Saxon,那么您可以改用 XPath 2.0 *:a/*:b
语法。无论哪种方式,您仍然需要在启用命名空间感知的情况下进行解析。
/*[local-name()='a']/*[local-name()='b']
在使用命名空间不感知工厂和命名空间感知工厂进行解析时都有效。此外,在 CLASSPATH 上有 Saxon 和没有它。所有四种组合都有效。
@MarcusJuniusBrutus 这个特定版本的 Saxon 可能会按照您希望的方式运行,但您仍在尝试执行规范未涵盖的操作,因此您不能抱怨与未来的更新中断...以上是关于如果 Saxon 在 CLASSPATH 上,则无法识别命名空间的 XPath 表达式失败的主要内容,如果未能解决你的问题,请参考以下文章
如果 DOM classList 包含特定类,请检查 Saxon-JS 2.1
如何在 Saxon 的 XQuery 中动态引用 XML 文件