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
Mã
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?
Có vẻ như một lỗi trong quá trình triển khai XPath. – obecker
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. –