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

Posted

技术标签:

【中文标题】如何在具有默认命名空间的 xml 文档上使用 XPath【英文标题】:How to use XPath on xml docs having default namespace 【发布时间】:2011-04-25 18:35:51 【问题描述】:

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

这是我的代码:

    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();
    

这是我的 xml:

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

【问题讨论】:

这看起来是同一个问题***.com/questions/543049/… XML namespace, JDOM, and XPath 【参考方案1】:

我编写了一个简单的NamespaceContext 实现 (here),这可能会有所帮助。它将Map&lt;String, String&gt; 作为输入,其中key 是前缀,value 是命名空间。

它遵循NamespaceContext 规范,您可以在unit tests 中看到它是如何工作的。

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"]

注意它依赖于Google Guava

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

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

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

http://download.oracle.com/javase/6/docs/api/javax/xml/namespace/NamespaceContext.html

您的代码如下所示:

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;
        

    


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

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

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();
        
    


【讨论】:

所以我将不得不转向命名空间场景。好吧,一个好主意,但我这样做会很痛苦。我有大量的代码目前正在通过使用 xpath 来处理没有命名空间的 xml。我必须添加默认命名空间以进行验证(通过 IDE 和以编程方式)目的。有没有什么办法可以一石两鸟?我的意思是我可能不必编辑所有的 xpath 表达式,同时可以在 IDE 中和以编程方式验证文档? 我想删除命名空间。在这种情况下,我不会遇到 xpath 问题,并且对于编程验证,我可能会在运行时添加命名空间。也许,我只需要在验证之前解析我的文档。这可能是可以接受的,但这样做之后,我看不到任何方法可以通过 IDE 验证我的 xml 文档。还有其他想法吗? 将您的 XPath 更改为 Dennis 建议的内容将使您的原始代码正常工作。不使用命名空间方法。 哦,真的吗?那么一定还有其他一些错误,因为这段代码在我的机器上不起作用。你能帮我找到那个吗? 我添加了适用于我的版本,它使用非命名空间感知 DocumentBuilderFactory。

以上是关于如何在具有默认命名空间的 xml 文档上使用 XPath的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Nokogiri Builder 创建具有命名空间根元素的 XML 文档

具有命名空间的 XML 文档上的 XPath

在 C# 中使用具有默认命名空间的 Xpath

如何在 C# 中创建具有命名空间的文档

如何在 C# 中创建具有命名空间的文档

使用 DOM 解析器在 Java 中解析具有 2 个默认命名空间的 XML