Weijun Liu created JXPATH-207:
---------------------------------

             Summary: DOMNodePointer.asPath() can point to the wrong sibling 
for a namespaced element
                 Key: JXPATH-207
                 URL: https://issues.apache.org/jira/browse/JXPATH-207
             Project: Commons JXPath
          Issue Type: Bug
    Affects Versions: 1.4.0
            Reporter: Weijun Liu


h3. Summary

DOMNodePointer.asPath() may return an XPath that points to a different
sibling when the target is a namespaced element for which no usable prefix
can be resolved, and a text, comment, or processing-instruction node appears
before that element.

In this case DOMNodePointer.asPath() falls back to node()[N], but N is
calculated by getRelativePositionOfElement(), which counts only element
siblings. In XPath 1.0, node()[N] counts all matching child nodes, including
text, comment, and processing-instruction nodes.

As a result, evaluating the XPath returned by asPath() may select a different
node.

h3. Minimal reproducer

{code:java}
import static org.junit.jupiter.api.Assertions.assertSame;

import java.io.StringReader;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.Pointer;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

class DOMNodePointerAsPathTest {

    @Test
    void asPathMustPointBackToTheNamespacedElement() throws Exception {
        DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        Document document = factory.newDocumentBuilder().parse(
                new InputSource(new StringReader(
                        "<root>before<item xmlns=\"urn:test\"/></root>")));

        Element target =
                (Element) document.getDocumentElement().getLastChild();

        JXPathContext context = JXPathContext.newContext(document);
        Pointer pointer = context.getPointer("/root/*");

        assertSame(target, pointer.getNode());

        String path = pointer.asPath();

        // Fails: path is "/root[1]/node()[1]" and resolves to the
        // preceding text node rather than the target element.
        assertSame(target, context.getPointer(path).getNode(), path);
    }
}
{code}

h3. Actual behavior

pointer.asPath() returns:

{code}
/root[1]/node()[1]
{code}

Evaluating this path selects the preceding text node containing "before",
rather than the target <item> element.

h3. Expected behavior

The XPath returned by Pointer.asPath() should represent the same concrete
node and should resolve back to that node in the same JXPathContext.

For the example above, valid results would include:

{code}
/root[1]/node()[2]
{code}

or:

{code}
/root[1]/*[1]
{code}

h3. Root cause

In DOMNodePointer.asPath(), the fallback for a namespaced element without a
resolvable prefix emits:

{code:java}
buffer.append("node()");
buffer.append('[');
buffer.append(getRelativePositionOfElement());
{code}

However, getRelativePositionOfElement() increments the position only for
Node.ELEMENT_NODE siblings.

The node() node test matches all child node types, so the emitted node()
predicate and the calculated position use different sibling sets.

JXPATH-114 corrected node() so that it matches every applicable DOM/JDOM
node, but the element-only position calculation used by asPath() was not
updated.


h3. Affected versions

Confirmed in Commons JXPath 1.4.0 and current master
(7901c0e709ca6fd8ce96ea17d2410c40959c84a1).

The underlying path-generation pattern is also present in the 1.2 and 1.3
code lines.

h3. Related issues

* JXPATH-114 fixed node() matching semantics but did not update asPath().
* JXPATH-154 concerns an infinite loop in namespace prefix resolution.
* JXPATH-5/JXPATH-71 concern pointer reuse while iterating collections.
* JXPATH-182 concerns malformed paths for dynamic properties.

None of these issues covers the mismatch between node()[N] and the
element-only sibling position used by DOMNodePointer.asPath().



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to