NamespaceContext并使用带有XPath的命名空间

解析包含Java中的命名空间的xpath似乎需要使用NamespaceContext对象,将前缀映射到命名空间URL,反之亦然。 但是,除了自己实现之外,我找不到获取NamespaceContext机制。 这似乎违反直觉。

问题:是否有任何简单的方法从文档中获取NamespaceContext ,或创建一个,或者失败,完全放弃前缀并指定具有完全限定名称的xpath?

可以在不编写自己的类的情况下获取NamespaceContext实例。 它的类使用页面显示您可以使用javax.xml.stream包获取一个。

 String ctxtTemplate = ""; NamespaceContext nsContext = null; XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader evtReader = factory .createXMLEventReader(new StringReader(ctxtTemplate)); while (evtReader.hasNext()) { XMLEvent event = evtReader.nextEvent(); if (event.isStartElement()) { nsContext = ((StartElement) event) .getNamespaceContext(); break; } } System.out.println(nsContext.getNamespaceURI("")); System.out.println(nsContext.getNamespaceURI("foo")); System.out.println(nsContext .getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE)); System.out.println(nsContext .getNamespaceURI(XMLConstants.XML_NS_PREFIX)); 

完全放弃前缀可能会导致表达式模糊 – 如果要删除名称空间前缀,则需要更改文档格式。 从文档创建上下文不一定有意义。 前缀必须与XPath表达式中使用的前缀匹配,而不是与任何文档中的前缀相匹配,如下面的代码所示:

 String xml = "" + "" + "hello" + "" + ""; String expression = "/stack:data/overflow:value"; class BaseFooContext implements NamespaceContext { @Override public String getNamespaceURI(String prefix) { if ("stack".equals(prefix)) return "http://base"; if ("overflow".equals(prefix)) return "http://foo"; throw new IllegalArgumentException(prefix); } @Override public String getPrefix(String namespaceURI) { throw new UnsupportedOperationException(); } @Override public Iterator getPrefixes( String namespaceURI) { throw new UnsupportedOperationException(); } } XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new BaseFooContext()); String value = xpath.evaluate(expression, new InputSource(new StringReader(xml))); System.out.println(value); 

StAX API返回的实现和上面的实现都没有实现doc中定义的完整类 / 方法契约。 您可以在此处获得完整的基于地图的实施。

我自己一直在使用xpath和NamespaceContexts。 我在developerworks上遇到了一个很好的处理问题。

我在名为NamespaceContextImpl的“ Apache WebServices Common Utilities ”中找到了一个方便的实现。

您可以使用以下maven依赖项来获取此类:

  org.apache.ws.commons ws-commons-util 1.0.1  

我以下面的方式使用它(我知道它是为sax构建的,但在阅读完代码之后,它还可以):

 NamespaceContextImpl nsContext = new NamespaceContextImpl(); nsContext.startPrefixMapping("foo", "my.name.space.com"); 

您不需要调用endPrefixMapping。

如果您使用的是Spring框架,则可以重用其NamespaceContext实现org.springframework.util.xml.SimpleNamespaceContext

这是一个类似于Asaf Mesika的答案。 因此它不会根据您的文档自动为您提供NamespaceContext。 你必须自己构建它。 它仍然可以帮助你,因为它至少为你提供了一个实现。

当我们遇到类似的问题时,Spring SimpleNamespaceContext和“Apache WebServices Common Utilities”都有效。 我们希望避免添加jar依赖于Apache WebServices Common Utilities并使用Spring,因为我们的应用程序是基于Spring的。

如果您使用的是Jersey 2并且只有一个默认的XML命名空间( xmlns="..." ),则可以使用SimpleNamespaceResolver :

     
 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document document = docBuilder.parse(new File("document.xml")); String query = "/t:Outer/t:Inner"; XPath xpath = XPathFactory.newInstance().newXPath(); String xmlns = document.getDocumentElement().getAttribute("xmlns"); xpath.setNamespaceContext(new SimpleNamespaceResolver("t", xmlns)); NodeList nodeList = (NodeList) xpath.evaluate(query, document, XPathConstants.NODESET); //nodeList will contain the  element 

如果需要,还可以手动指定xmlns