4

给定以下 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但它在其属性中采用节点而不是文档。这将打印出以下结果:

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

4

1 回答 1

3

XPath 表达式//name绝对路径(以 a 开头/),因此选择包含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

于 2012-12-18T11:51:15.933 回答