Hi all,
I'm trying to learn how to use JXPath with DOM in order to speed up some
code that uses a lot of xpath. I've seen blog posts suggesting it's
about twice as fast as JAXP's XPath processor...
The problem I'm running into is when I construct a JXPathContext around
a node down in the DOM tree, then try to select a node elsewhere in the
tree using the ancestor:: axis. I'm attaching a sample XML file and unit
test that shows what I'm trying to do.
I've run this through a debugger, and it appears that the
DOMNodePointer.getImmediateParent() doesn't even try to look at the
Node.getParentNode()...if it doesn't have a pre-existing parent (from
its ctor) then it just dumbly returns the null parent.
I haven't done enough research yet to know how to get DOMNodePointer to
populate its parent (using the public API, not the nuts-and-bolts impl
details), but in the attached example you can see I try two approaches:
1. the naive approach, which is also the last one in the code. IMO, this
one should work!
2. a brute-force alternative, where JXPathContext instances for each
intermediate node are created to inherit in the right order, all the way
back to the document itself. From my partial reading of the code, this
should work even if the naive approach doesn't.
Neither of these works, though. Can someone shed some light on it, or
let me know if I've found a bug (seems like a common use case)...
Thanks,
-john
--
John Casey
GitHub - http://github.com/jdcasey
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId>
<artifactId>test-project</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.group</groupId>
<artifactId>artifact-id</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
package org.commonjava.maven.galley.maven.model.view;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.List;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.jxpath.JXPathContext;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class JXPathContextAncestryTest
{
@Test
public void basicJXPathTest()
throws Exception
{
final InputStream is = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(
"jxpath/simple.pom.xml" );
final Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse( is );
final JXPathContext ctx = JXPathContext.newContext( document );
document.getDocumentElement()
.removeAttribute( "xmlns" );
final String projectGroupIdPath = "ancestor::project/groupId";
// NOT what's failing...just populating the node set to traverse in
order to feed the ancestor:: axis test.
final List<?> nodes = ctx.selectNodes(
"/project/dependencies/dependency" );
for ( final Object object : nodes )
{
final Node node = (Node) object;
dump( node );
final Stack<Node> revPath = new Stack<Node>();
Node parent = node;
while ( parent != null )
{
revPath.push( parent );
parent = parent.getParentNode();
}
JXPathContext nodeCtx = null;
while ( !revPath.isEmpty() )
{
final Node part = revPath.pop();
if ( nodeCtx == null )
{
nodeCtx = JXPathContext.newContext( part );
}
else
{
nodeCtx = JXPathContext.newContext( nodeCtx, part );
}
}
System.out.println( "Path derived from context: '" +
nodeCtx.getNamespaceContextPointer()
.asPath() + "'" );
// brute-force approach...try to force population of the parent
pointers by painstakingly constructing contexts for all intermediate nodes.
System.out.println( "Selecting groupId for declaring project using
path-derived context..." );
System.out.println( nodeCtx.getValue( projectGroupIdPath ) );
// Naive approach...this has all the context info it needs to get
parent contexts up to and including the document!
System.out.println( "Selecting groupId for declaring project using
non-derived context..." );
System.out.println( JXPathContext.newContext( node )
.getValue( projectGroupIdPath ) );
}
}
private DocumentBuilder docBuilder;
private Transformer transformer;
@Before
public void setup()
throws Exception
{
docBuilder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2" );
}
protected void dump( final Node node )
throws Exception
{
if ( node == null )
{
System.out.println( "Cannot dump null node." );
return;
}
final StringWriter sw = new StringWriter();
transformer.transform( new DOMSource( docBuilder.newDocument()
.importNode( node, true
) ), new StreamResult( sw ) );
System.out.println( sw );
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]