Why does javax.xml.xpath.XPath act differently with a cloned node?

Given the following XML (basic.xml):

<rdr> <details> <detail> <name>version</name> <value>15.0</value> </detail> <detail> <name>resolution</name> <value>1080X1920</value> </detail> </details> </rdr> 

I want to get the name and versions, so I have the following code. This is not very neat, but I created it for illustrative purposes, but the code works completely:

 import java.io.FileInputStream; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class Example { private static XPath factoryXpath = null; public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { FileInputStream fin = new FileInputStream("basic.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(fin); XPathFactory xPathFactory = XPathFactory.newInstance(); factoryXpath = xPathFactory.newXPath(); printDetails(document); } public static void printDetails(Node node) throws XPathExpressionException { NodeList nodes = (NodeList) factoryXpath.evaluate("//detail", node, XPathConstants.NODESET); printNameAndValue(nodes.item(0)); printNameAndValue(nodes.item(1)); } public static void printNameAndValue(Node node) throws XPathExpressionException { System.out.println("Name=" + (String) factoryXpath.evaluate("//name", node, XPathConstants.STRING)); System.out.println("Value=" + (String) factoryXpath.evaluate("//value", node, XPathConstants.STRING)); } } 

Outputs the following:

 Name=version Value=15.0 Name=version Value=15.0 

Why does it print the same name and value both times?

If I clone node first, so printNameAndValue now looks like this:

 public static void printNameAndValue(Node node) throws XPathExpressionException { Node clonedNode = node.cloneNode(true); System.out.println("Name=" + (String) factoryXpath.evaluate("//name", clonedNode, XPathConstants.STRING)); System.out.println("Value=" + (String) factoryXpath.evaluate("//value", clonedNode, XPathConstants.STRING)); } 

I get the following output:

 Name=version Value=15.0 Name=resolution Value=1080X1920 

Why does a cloned node act differently?

I deleted the cloned node and returned to the original example when it wasn’t working, and added the method described here https://stackoverflow.com/a/2128167/2208 , but with this, accepting the node instead of the document in its attributes. This produces the following result:

 <?xml version="1.0" encoding="UTF-8"?><detail> <name>version</name> <value>15.0</value> </detail> Name=version Value=15.0 <?xml version="1.0" encoding="UTF-8"?><detail> <name>resolution</name> <value>1080X1920</value> </detail> Name=version Value=15.0 

From this it is clear that node is the one we expect; but it applies XPath to the first node, or perhaps to the original document. I am fine with cloning a node and using this, but I would really like to know why this is happening.

+4
source share
1 answer

The XPath //name expression is an absolute path (starting with / ), so a node set is selected that contains all the name elements in the document to which the node context belongs. Thus, evaluating this expression as a string according to the XPath 1.0 data model, you get the string value of the first such node in the document order.

An important part of this first sentence is the "document to which the node context belongs" - the cloned node is not attached to the document, therefore the XPath evaluator considers the node itself as the root fragment of the document and evaluates the expression against this fragment (which contains only one name element) instead of the original document (which contains two).

If printNameAndValue uses relative XPath expressions instead

 public static void printNameAndValue(Node node) throws XPathExpressionException { System.out.println("Name=" + (String) factoryXpath.evaluate("name", node, XPathConstants.STRING)); System.out.println("Value=" + (String) factoryXpath.evaluate("value", node, XPathConstants.STRING)); } 

(or .//name , if the name element can be a grandson or deeper, rather than an immediate child), then you should get the expected result, that is, the value of the first name (respectively value ) is the child of the element of the specified node .

+3
source

All Articles