【问题标题】:How to use XPath on xml docs having default namespace如何在具有默认命名空间的 xml 文档上使用 XPath
【发布时间】: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>

【问题讨论】:

标签: java xml 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。您使用的前缀不需要与文档中使用的前缀匹配。

      您的代码如下所示:

      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。
      猜你喜欢
      • 2014-09-17
      • 2012-12-29
      • 2010-10-08
      • 2011-10-09
      • 2010-10-09
      相关资源
      最近更新 更多