2013-07-24 39 views
6

在以下任何XPath的示例代碼的形式爲當源XML有一個命名空間前綴「// 的ElementName」返回NULL(見testWithNS()在底部的代碼)。爲什麼只有特定的XPath表達式查找節點時,XML有一個命名空間前綴

如果源xml沒有名稱空間前綴,則列出的所有XPath表達式將返回一個節點(請參見testNoNS())。

我知道我可以通過設置NamespaceContext(如testWithNSContext())解析XML,解析xml作爲名稱空間感知文檔,並在XPath中使用名稱空間前綴。但是我不想這樣做,因爲我的實際代碼需要處理帶或不帶名稱空間前綴的xml。

我的問題是,爲什麼只有是:

  • //測試
  • // child1
  • // grandchild1
  • //的child2

返回空值,但testWithNS()中的所有其他示例都返回節點?

輸出

testNoNS() 
test = found 
/test = found 
//test = found 
//test/* = found 
//test/child1 = found 
//test/child1/grandchild1 = found 
//test/child2 = found 
//child1 = found 
//grandchild1 = found 
//child1/grandchild1 = found 
//child2 = found 

testWithNS() 
test = found 
/test = found 
//test = *** NOT FOUND *** 
//test/* = found 
//test/child1 = found 
//test/child1/grandchild1 = found 
//test/child2 = found 
//child1 = *** NOT FOUND *** 
//grandchild1 = *** NOT FOUND *** 
//child1/grandchild1 = found 
//child2 = *** NOT FOUND *** 

testWithNSContext() 
ns1:test = found 
/ns1:test = found 
//ns1:test = found 
//ns1:test/* = found 
//ns1:test/ns1:child1 = found 
//ns1:test/ns1:child1/ns1:grandchild1 = found 
//ns1:test/ns1:child2 = found 
//ns1:child1 = found 
//ns1:grandchild1 = found 
//ns1:child1/ns1:grandchild1 = found 
//ns1:child2 = found 

代碼

import java.io.StringReader; 
import java.util.Iterator; 

import javax.xml.XMLConstants; 
import javax.xml.namespace.NamespaceContext; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathFactory; 

import org.junit.Test; 
import org.w3c.dom.Document; 
import org.xml.sax.InputSource; 

public class XPathBugTest { 

    private String xmlDec = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"; 
    private String xml = xmlDec + 
     "<test>" + 
     " <child1>" + 
     " <grandchild1/>" + 
     " </child1>" + 
     " <child2/>" + 
     "</test>"; 
    private String xmlNs = xmlDec + 
     "<ns1:test xmlns:ns1=\"http://www.wfmc.org/2002/XPDL1.0\">" + 
     " <ns1:child1>" + 
     " <ns1:grandchild1/>" + 
     " </ns1:child1>" + 
     " <ns1:child2/>" + 
     "</ns1:test>"; 

    final XPathFactory xpathFactory = XPathFactory.newInstance(); 
    final XPath xpath = xpathFactory.newXPath(); 

    @Test 
    public void testNoNS() throws Exception { 
     System.out.println("\ntestNoNS()"); 
     final Document doc = getDocument(xml); 

     isFound("test", xpath.evaluate("test", doc, XPathConstants.NODE)); 
     isFound("/test", xpath.evaluate("/test", doc, XPathConstants.NODE)); 
     isFound("//test", xpath.evaluate("//test", doc, XPathConstants.NODE)); 
     isFound("//test/*", xpath.evaluate("//test/*", doc, XPathConstants.NODE)); 
     isFound("//test/child1", xpath.evaluate("//test/child1", doc, XPathConstants.NODE)); 
     isFound("//test/child1/grandchild1", xpath.evaluate("//test/child1/grandchild1", doc, XPathConstants.NODE)); 
     isFound("//test/child2", xpath.evaluate("//test/child2", doc, XPathConstants.NODE)); 
     isFound("//child1", xpath.evaluate("//child1", doc, XPathConstants.NODE)); 
     isFound("//grandchild1", xpath.evaluate("//grandchild1", doc, XPathConstants.NODE)); 
     isFound("//child1/grandchild1", xpath.evaluate("//child1/grandchild1", doc, XPathConstants.NODE)); 
     isFound("//child2", xpath.evaluate("//child2", doc, XPathConstants.NODE)); 
    } 

    @Test 
    public void testWithNS() throws Exception { 
     System.out.println("\ntestWithNS()"); 
     final Document doc = getDocument(xmlNs); 

     isFound("test", xpath.evaluate("test", doc, XPathConstants.NODE)); 
     isFound("/test", xpath.evaluate("/test", doc, XPathConstants.NODE)); 
     isFound("//test", xpath.evaluate("//test", doc, XPathConstants.NODE)); 
     isFound("//test/*", xpath.evaluate("//test/*", doc, XPathConstants.NODE)); 
     isFound("//test/child1", xpath.evaluate("//test/child1", doc, XPathConstants.NODE)); 
     isFound("//test/child1/grandchild1", xpath.evaluate("//test/child1/grandchild1", doc, XPathConstants.NODE)); 
     isFound("//test/child2", xpath.evaluate("//test/child2", doc, XPathConstants.NODE)); 
     isFound("//child1", xpath.evaluate("//child1", doc, XPathConstants.NODE)); 
     isFound("//grandchild1", xpath.evaluate("//grandchild1", doc, XPathConstants.NODE)); 
     isFound("//child1/grandchild1", xpath.evaluate("//child1/grandchild1", doc, XPathConstants.NODE)); 
     isFound("//child2", xpath.evaluate("//child2", doc, XPathConstants.NODE)); 
    } 

    @Test 
    public void testWithNSContext() throws Exception { 
     System.out.println("\ntestWithNSContext()"); 
     final Document doc = getDocumentNS(xmlNs); 

     xpath.setNamespaceContext(new MyNamespaceContext()); 

     isFound("ns1:test", xpath.evaluate("ns1:test", doc, XPathConstants.NODE)); 
     isFound("/ns1:test", xpath.evaluate("/ns1:test", doc, XPathConstants.NODE)); 
     isFound("//ns1:test", xpath.evaluate("//ns1:test", doc, XPathConstants.NODE)); 
     isFound("//ns1:test/*", xpath.evaluate("//ns1:test/*", doc, XPathConstants.NODE)); 
     isFound("//ns1:test/ns1:child1", xpath.evaluate("//ns1:test/ns1:child1", doc, XPathConstants.NODE)); 
     isFound("//ns1:test/ns1:child1/ns1:grandchild1", xpath.evaluate("//ns1:test/ns1:child1/ns1:grandchild1", doc, XPathConstants.NODE)); 
     isFound("//ns1:test/ns1:child2", xpath.evaluate("//ns1:test/ns1:child2", doc, XPathConstants.NODE)); 
     isFound("//ns1:child1", xpath.evaluate("//ns1:child1", doc, XPathConstants.NODE)); 
     isFound("//ns1:grandchild1", xpath.evaluate("//ns1:grandchild1", doc, XPathConstants.NODE)); 
     isFound("//ns1:child1/ns1:grandchild1", xpath.evaluate("//ns1:child1/ns1:grandchild1", doc, XPathConstants.NODE)); 
     isFound("//ns1:child2", xpath.evaluate("//ns1:child2", doc, XPathConstants.NODE)); 
    } 

    private void isFound(String xpath, Object object) { 
     System.out.println(xpath + " = " + (object == null ? "*** NOT FOUND ***" : "found")); 
    } 

    private Document getDocument(final String xml) throws Exception { 
     final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
     return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));   
    } 

    private Document getDocumentNS(final String xml) throws Exception { 
     final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
     factory.setNamespaceAware(true); 
     return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); 
    } 

    public class MyNamespaceContext implements NamespaceContext { 
     @Override 
     public String getNamespaceURI(String prefix) { 
      if ("ns1".equals(prefix)) { 
       return "http://www.wfmc.org/2002/XPDL1.0"; 
      } 
      return XMLConstants.NULL_NS_URI; 
     } 
     @Override 
     public String getPrefix(String uri) { 
      throw new UnsupportedOperationException(); 
     } 
     @Override 
     public Iterator getPrefixes(String uri) { 
      throw new UnsupportedOperationException(); 
     } 
    } 
} 

更新以下撒克遜測試

我已經使用撒克遜改變XPahtFactory線到此

現在測試的相同碼
final XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl(); 

使用撒克遜人在testWithNS()回報*** NOT FOUND ***所有行,而不是像「// 的ElementName」只是那些與默認Xalan的實現。

鑑於我正在使用非名稱空間感知文檔生成器工廠來解析xml,爲什麼這些xpaths都沒有工作,只有一些Xalan?

+3

看起來像XPath實現中的錯誤。 – obecker

+1

我曾想過,但認爲它也可能是某人顯而易見的事情。我會用撒克遜人嘗試以上方法並報告發生了什麼。 –

回答

1

如果你想忽略的命名空間,你可以使用local-name XPath函數:

//*[local-name()='grandchild1'] 
+0

謝謝。我知道有這樣的方法來完成這項工作,但我的問題是**爲什麼**它可以與我的一些XPath協同工作,但不是其他方法? –

1

由於我使用的是無感知名稱空間文檔構建工廠來解析XML,爲什麼所有的這些xpaths的工作,只有一些與Xalan?

XPath語言僅在命名空間格式良好的XML文檔和片段上定義。如果您解析時沒有命名空間支持,那麼所有投注都關閉,並且不保證任何XPath表達式在由非名稱空間感知解析器創建的DOM上正常工作(即使相關文檔不使用任何名稱空間) 。

我知道我在Java內置的XSLT處理器中給出非NS的文檔時,發現了非常不一致的行爲。

+0

有趣。你能否引用一個資料來源,獲取更多信息? (特別是「XPath語言僅在...上定義」) – LarsH

+1

@LarsH「由XPath操作的XML文檔必須符合XML名稱空間建議書[XML名稱]。」 ([XPath規範,第5節「數據模型」](http://www.w3.org/TR/xpath/#data-model)) –

相關問題