当 xml 具有命名空间前缀时,为什么只有某些 XPath 表达式才能找到节点 [英] Why do only certain XPath expressions find nodes when xml has a namespace prefix

查看:22
本文介绍了当 xml 具有命名空间前缀时,为什么只有某些 XPath 表达式才能找到节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的示例代码中,当源 xml 具有命名空间前缀时,任何形式为//elementName"的 XPath 将返回 null(请参阅底部的代码).

In the example code below any XPath that are in the form '//elementName' return null when the source xml has a namespace prefix (see testWithNS() in the code at the bottom).

当源 xml 没有命名空间前缀时,所有列出的 XPath 表达式都返回一个节点(请参阅 testNoNS()).

When the source xml does not have a namespace prefix all the listed XPath expressions return a node (see testNoNS()).

我知道我可以通过设置 NamespaceContext(如在 testWithNSContext() 中)、将 xml 解析为命名空间感知文档并在 XPath 中使用命名空间前缀来解决这个问题.但是我不想这样做,因为我的实际代码需要处理带有和不带有名称空间前缀的 xml.

I know I could solve this by setting up a NamespaceContext (as in testWithNSContext()), parsing the xml as a namespace aware document, and using namespace prefixes in the XPaths. However I don't want to do this as my actual code needs to process xml both with and without namespace prefixes.

我的问题是为什么它只是:

My question is why is it only:

  • //测试
  • //child1
  • //grandchild1
  • //child2

返回空值,而 testWithNS() 中的所有其他示例都返回节点?

that return null, yet all other examples in testWithNS() return the node?

输出

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 行更改为此

Update following Saxon test

I have now tested the same code using Saxon changing the XPahtFactory line to this

final XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl();

testWithNS() 中使用 Saxon 的所有行都会返回 *** NOT FOUND *** 而不仅仅是像 '//elementName 这样的行>' 与默认的 Xalan 实现一样.

Using Saxon all lines in testWithNS() return *** NOT FOUND *** rather than just the ones like '//elementName' as with the default Xalan implementation.

鉴于我使用非命名空间感知文档生成器工厂来解析 xml,为什么这些 xpath 都不起作用,而只有一些与 Xalan 一起使用?

Given that I'm using a non namespace aware document builder factory to parse the xml, why do none of these xpaths work, and only some with Xalan?

推荐答案

如果想忽略命名空间,可以使用 local-name XPath 函数:

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

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

这篇关于当 xml 具有命名空间前缀时,为什么只有某些 XPath 表达式才能找到节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆