给出以下XML(basic.xml):
<rdr>
<details>
<detail>
<name>version</name>
<value>15.0</value>
</detail>
<detail>
<name>resolution</name>
<value>1080X1920</value>
</detail>
</details>
</rdr>
我想获取名称和版本,所以我有以下代码。这不是很整洁,但我为了说明的目的创建了这个,但代码完全起作用:
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));
}
}
这输出以下内容:
Name=version
Value=15.0
Name=version
Value=15.0
为什么它两次输出相同的名称和值?
如果我首先克隆节点,那么printNameAndValue现在看起来像这样:
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));
}
我得到以下输出:
Name=version
Value=15.0
Name=resolution
Value=1080X1920
为什么克隆节点的行为不同?
我删除了克隆节点并恢复到原始示例,但它不起作用并添加了此处描述的方法https://stackoverflow.com/a/2325407/211560,但是在其属性中使用了Node而不是Document。这将打印出以下结果:
<?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
由此可以清楚地看到节点是我们期望的节点;但它将XPath应用于第一个节点,或者可能应用于原始文档。我可以克隆节点并使用它,但我真的很想知道为什么会发生这种情况。
答案 0 :(得分:3)
XPath表达式//name
是绝对路径(以/
开头),因此选择包含文档中所有name
个元素的节点集上下文节点所属的。因此,根据XPath 1.0数据模型将该表达式作为字符串进行评估,将按文档顺序为您提供第一个此类节点的字符串值。
第一句话的关键部分是“上下文节点所属的文档” - 克隆节点未附加到文档,因此XPath评估程序将节点本身视为文档片段的根,并评估针对该片段的表达式(仅包含一个name
元素)而不是原始文档(包含两个)。
如果在printNameAndValue
中你使用了相对的XPath表达式
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));
}
(或.//name
如果name
元素可能是孙子或更深层而不是直接孩子)那么你应该得到你期望的输出,即第一个name
的值(分别为value
)指定node
的元素子。