如果 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 树上的行为(即使是像 &lt;a&gt;&lt;b/&gt;&lt;/a&gt; 这样的,如果它在命名空间感知中解析方式,实际上不会使用任何命名空间)充其量是特定于实现的,最坏的情况是完全未定义。

【讨论】:

我需要对命名空间保持宽容,因为我必须处理并非总是 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

编译样式表时的未知函数 saxon:parse-html

如何在 Saxon 的 XQuery 中动态引用 XML 文件

Oracle 12 XMLElement:如果 value_expr 为空,则无标记

使用 Saxon 对 Msi 文件 (xml) 运行 XSL 转换