Java 如何在具有默认命名空间的 xml 文档上使用 XPath

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3939636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 07:14:38  来源:igfitidea点击:

How to use XPath on xml docs having default namespace

javaxmlxpath

提问by WSK

I want to manipulate xml doc having default namespace but no prefix. Is there a way to use xpath without namespace uri just as if there is no namespace?
I believe it should be possible if we set namespaceAware property of documentBuilderFactory to false. But in my case it is not working.
Is my understanding is incorrect or I am doing some mistake in code?

我想操作具有默认名称空间但没有前缀的 xml 文档。有没有办法在没有命名空间 uri 的情况下使用 xpath,就像没有命名空间一样?
我相信如果我们将 documentBuilderFactory 的 namespaceAware 属性设置为 false 应该是可能的。但就我而言,它不起作用。
是我的理解不正确还是我在代码中犯了一些错误?

Here is my code:

这是我的代码:

    DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
    domFactory.setNamespaceAware(false);
    try {
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document dDoc = builder.parse("E:/test.xml");

        XPath xPath = XPathFactory.newInstance().newXPath();
        NodeList nl = (NodeList) xPath.evaluate("//author", dDoc, XPathConstants.NODESET);
        System.out.println(nl.getLength());
    } catch (Exception e) {
        e.printStackTrace();
    }

Here is my xml:

这是我的 xml:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.mydomain.com/schema">
  <author>
    <book title="t1"/>
    <book title="t2"/>
  </author>
</root>

采纳答案by bdoughan

The XPath processing for a document that uses the default namespace (no prefix) is the same as the XPath processing for a document that uses prefixes:

使用默认命名空间(无前缀)的文档的 XPath 处理与使用前缀的文档的 XPath 处理相同:

For namespace qualified documents you can use a NamespaceContext when you execute the XPath. You will need to prefix the fragments in the XPath to match the NamespaceContext. The prefixes you use do not need to match the prefixes used in the document.

对于命名空间限定的文档,您可以在执行 XPath 时使用 NamespaceContext。您需要在 XPath 中为片段添加前缀以匹配 NamespaceContext。您使用的前缀不需要与文档中使用的前缀相匹配。

Here is how it looks with your code:

以下是您的代码的外观:

import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class Demo {

    public static void main(String[] args) {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        try {
            DocumentBuilder builder = domFactory.newDocumentBuilder();
            Document dDoc = builder.parse("E:/test.xml");

            XPath xPath = XPathFactory.newInstance().newXPath();
            xPath.setNamespaceContext(new MyNamespaceContext());
            NodeList nl = (NodeList) xPath.evaluate("/ns:root/ns:author", dDoc, XPathConstants.NODESET);
            System.out.println(nl.getLength());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class MyNamespaceContext implements NamespaceContext {

        public String getNamespaceURI(String prefix) {
            if("ns".equals(prefix)) {
                return "http://www.mydomain.com/schema";
            }
            return null;
        }

        public String getPrefix(String namespaceURI) {
            return null;
        }

        public Iterator getPrefixes(String namespaceURI) {
            return null;
        }

    }

}

Note: I also used the corrected XPath suggested by Dennis.

注意:我还使用了Dennis建议的更正的 XPath 。

The following also appears to work, and is closer to your original question:

以下似乎也有效,并且更接近您的原始问题:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class Demo {

    public static void main(String[] args) {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = domFactory.newDocumentBuilder();
            Document dDoc = builder.parse("E:/test.xml");

            XPath xPath = XPathFactory.newInstance().newXPath();
            NodeList nl = (NodeList) xPath.evaluate("/root/author", dDoc, XPathConstants.NODESET);
            System.out.println(nl.getLength());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

回答by WSK

Blaise Doughanis right, attached code is correct.
Problem was somewhere elese. I was running all my tests through Application launcher in Eclipse IDE and nothing was working. Then I discovered Eclipse project was cause of all grief. I ran my class from command prompt, it worked. Created a new eclipse project and pasted same code there, it worked there too. Thank you all guys for your time and efforts.

Blaise Doughan是对的,附加的代码是正确的。
问题出在其他地方。我通过 Eclipse IDE 中的应用程序启动器运行所有测试,但没有任何效果。然后我发现 Eclipse 项目是所有悲伤的原因。我从命令提示符运行我的课程,它有效。创建了一个新的 eclipse 项目并在那里粘贴了相同的代码,它也在那里工作。感谢大家的时间和努力。

回答by tomaj

I've written a simple NamespaceContextimplementation (here), that might be of help. It takes a Map<String, String>as input, where the keyis a prefix, and the valueis a namespace.

我写了一个简单的NamespaceContext实现(这里),这可能会有所帮助。它接受 aMap<String, String>作为输入,其中key是前缀,value是命名空间。

It follows the NamespaceContextspesification, and you can see how it works in the unit tests.

它遵循NamespaceContext规范,您可以在单元测试中看到它是如何工作的。

Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "http://foo");
mappings.put("foo2", "http://foo");
mappings.put("bar", "http://bar");

context = new SimpleNamespaceContext(mappings);

context.getNamespaceURI("foo");    // "http://foo"
context.getPrefix("http://foo");   // "foo" or "foo2"
context.getPrefixes("http://foo"); // ["foo", "foo2"]

Note that it has a dependency on Google Guava

请注意,它依赖于Google Guava