2013-07-24 29 views
6

Trong mã ví dụ bên dưới bất kỳ XPath nào có dạng '// elementName' trả về null khi nguồn xml có tiền tố vùng tên (xem testWithNS() trong mã ở dưới cùng).Tại sao chỉ một số biểu thức XPath tìm thấy các nút khi xml có tiền tố không gian tên

Khi nguồn xml không có tiền tố không gian tên, tất cả các biểu thức XPath được liệt kê sẽ trả về một nút (xem testNoNS()).

Tôi biết tôi có thể giải quyết vấn đề này bằng cách thiết lập NamespaceContext (như trong testWithNSContext()), phân tích cú pháp xml dưới dạng tài liệu nhận biết vùng tên và sử dụng tiền tố không gian tên trong XPath. Tuy nhiên tôi không muốn làm điều này vì mã thực sự của tôi cần xử lý xml cả hai có và không có tiền tố không gian tên.

Câu hỏi của tôi là lý do duy nhất là nó:

  • // test
  • // child1
  • // grandchild1
  • // child2

mà trở về null, nhưng tất cả các ví dụ khác trong số testWithNS() trả lại nút?

Output

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

Cập nhật sau thử nghiệm Saxon

bây giờ tôi đã thử nghiệm cùng mã sử dụng Saxon thay đổi dòng XPahtFactory này

final XPathFactory xpathFactory = new net.sf.saxon.xpath.XPathFactoryImpl(); 

Sử dụng Saxon tất cả các dòng trong testWithNS() trả lại *** NOT FOUND *** thay vì chỉ những cái như '// elementName' như với thực thi Xalan mặc định.

Vì tôi đang sử dụng nhà máy xây dựng tài liệu nhận biết không gian tên để phân tích xml, tại sao không có xpath nào hoạt động và chỉ một số Xalan?

+3

Có vẻ như một lỗi trong quá trình triển khai XPath. – obecker

+1

Tôi đã tự hỏi điều đó nhưng nghĩ rằng nó cũng có thể là điều hiển nhiên đối với một ai đó. Tôi sẽ thử những điều trên với Saxon và báo cáo lại những gì sẽ xảy ra. –

Trả lời

1

Nếu bạn muốn bỏ qua không gian tên, bạn có thể sử dụng chức năng local-name XPath:

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

Cảm ơn. Tôi biết có những cách như thế này để thực hiện công việc này nhưng câu hỏi của tôi là ** tại sao ** nó hoạt động với một số XPath của tôi nhưng không làm việc với XPath của tôi? –

1

Cho rằng tôi đang sử dụng một nhà máy không namespace biết xây dựng tài liệu để phân tích xml, tại sao không ai trong số những xpath này hoạt động, và chỉ một số Xalan?

Ngôn ngữ XPath chỉ được xác định trên các tài liệu và đoạn mã XML được tạo đúng không gian tên. Nếu bạn phân tích cú pháp không hỗ trợ không gian tên thì tất cả các phiên cược sẽ bị tắt, sẽ không đảm bảo rằng bất kỳ biểu thức XPath nào sẽ hoạt động chính xác trên DOM được tạo bởi trình phân tích cú pháp không nhận biết không gian tên (ngay cả khi tài liệu được đề cập không sử dụng bất kỳ không gian tên nào) .

Tôi biết tôi đã nhìn thấy hành vi không nhất quán cực kỳ từ bộ xử lý XSLT tích hợp sẵn Java khi cung cấp tài liệu không phải NS.

+0

Thú vị. Bạn có thể trích dẫn nguồn, để biết thêm thông tin? (esp "Ngôn ngữ XPath chỉ được định nghĩa trên ...") – LarsH

+1

@LarsH "Các tài liệu XML được XPath vận hành phải tuân thủ các Khuyến nghị không gian tên XML [Tên XML]." ([Đặc tả XPath, phần 5 "Mô hình Dữ liệu"] (http://www.w3.org/TR/xpath/#data-model)) –

Các vấn đề liên quan