9

以下のサンプル コードでは、'// elementName ' の形式の XPath は、ソース xml に名前空間プレフィックスがある場合に null を返します (testWithNS()下部のコードを参照)。

ソース xml にネームスペース プレフィックスがない場合、リストされているすべての XPath 式はノードを返します (「 」を参照testNoNS())。

NamespaceContext を設定し (のようにtestWithNSContext())、xml を名前空間認識ドキュメントとして解析し、XPath で名前空間プレフィックスを使用することで、これを解決できることはわかっています。ただし、実際のコードでは、名前空間プレフィックスの有無にかかわらず xml を処理する必要があるため、これを行いたくありません。

私の質問は、なぜそれだけなのかです:

  • //テスト
  • //子1
  • //孫1
  • //子2

それはnullを返しますが、他のすべての例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 行をこれに変更して、Saxon を使用して同じコードをテストしました。

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

デフォルトの Xalan 実装のように '// elementName ' のような行だけではなく、Saxon のすべての行をtestWithNS()返します。*** NOT FOUND ***

名前空間を認識しないドキュメント ビルダー ファクトリを使用して xml を解析しているのに、これらの xpath がどれも機能せず、Xalan では一部しか機能しないのはなぜですか?

4

2 に答える 2

3

名前空間を無視したい場合は、local-nameXPath 関数を使用できます。

//*[local-name()='grandchild1']
于 2013-07-24T13:30:24.637 に答える