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

问题描述:

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

这里是我的代码:

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> 
+0

依赖这看起来像同样的问题 Default XML namespace, JDOM, and XPath belwood 2010-10-15 05:16:54

为使用默认的名称空间(无前缀)的文档中的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(); 
     } 
    } 

} 
+1

+1很好的解释了常见问题。 – 2010-10-15 14:13:09

+0

所以我将不得不移到命名空间的场景。那么,一个好主意,但我会这样做。我有大量的代码,目前正在通过使用xpath来处理没有命名空间的xml。我不得不添加默认命名空间进行验证(通过IDE和编程)的目的。一箭双雕可以杀死两只鸟吗?我的意思是我可能不需要编辑所有的xpath表达式,同时可以在IDE中以编程方式验证文档。 – WSK 2010-10-15 14:59:09

+0

我想删除命名空间。在这种情况下,我不会面对xpath问题和程序性验证,我可能会在运行时添加命名空间。也许,我只需要在验证之前解析我的文档。这可能是可以接受的,但这样做后,我看不到任何方式来验证我的XML文档的IDE。 有没有其他想法? – WSK 2010-10-15 15:00:52

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

我写了一个简单的NamespaceContext实现(here),这可能是有帮助的。它需要一个Map<String, String>作为输入,其中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