为什么javax.xml.xpath.XPath对克隆节点的行为不同?

时间:2012-12-18 11:38:32

标签: java

给出以下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应用于第一个节点,或者可能应用于原始文档。我可以克隆节点并使用它,但我真的很想知道为什么会发生这种情况。

1 个答案:

答案 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的元素子。