简体   繁体   English

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

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

Given the following XML (basic.xml): 给定以下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 out, so I have the following code. 我想得到名称和版本,所以我有以下代码。 This isn't very tidy, but I have created this for illustrative purposes, but the code does fully function: 这不是很整洁,但我已经创建了这个用于说明目的,但代码完全起作用:

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));
    }

}

This outputs the following: 这输出如下:

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

Why does it output the same Name and Value both times? 为什么它两次都输出相同的名称和值?

If I clone the node first, so that printNameAndValue now looks like this: 如果我首先克隆节点,那么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));
}

I get the following output: 我得到以下输出:

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

Why does a cloned node act differently? 为什么克隆节点的行为不同?

I removed the cloned node and reverted to the original example where it doesn't work and added the method described here https://stackoverflow.com/a/2325407/211560 but with it taking a Node instead of a Document in its attributes. 我删除了克隆节点并恢复到原始示例,它不起作用,并添加了此处描述的方法https://stackoverflow.com/a/2325407/211560,但它在其属性中采用节点而不是文档。 This prints out 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

It is clear from this that the node is the one we would expect; 由此可以清楚地看出节点是我们期望的节点; but it is applying the XPath to the first node, or maybe to the original document. 但它将XPath应用于第一个节点,或者可能应用于原始文档。 I'm OK with cloning a node and using that but I'd really like to know why this is happening. 我可以克隆节点并使用它,但我真的很想知道为什么会发生这种情况。

The XPath expression //name is an absolute path (beginning with a / ), so selects a node set containing all name elements in the document to which the context node belongs. XPath表达式//name绝对路径(以/开头),因此选择包含上下文节点所属文档中所有name元素的节点集。 Thus evaluating that expression as a string according to the XPath 1.0 data model will give you the string value of the first such node in document order. 因此,根据XPath 1.0数据模型将该表达式计算为字符串将为您提供文档顺序中第一个此类节点的字符串值。

The crucial part of that first sentence is "the document to which the context node belongs" - a cloned node is not attached to a document, so the XPath evaluator treats the node itself as the root of a document fragment and evaluates the expression against that fragment (which contains only one name element) instead of against the original document (which contains two). 第一句话的关键部分是“上下文节点所属的文档” - 克隆节点未附加到文档,因此XPath评估程序将节点本身视为文档片段的根,并针对该文档片段评估表达式fragment(仅包含一个name元素)而不是原始文档(包含两个)。

If in printNameAndValue you instead used relative XPath expressions 如果在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));
}

(or .//name if the name element might be a grandchild or deeper rather than an immediate child) then you should get the output you expect, ie the value of the first name (respectively value ) element child of the specified node . (或.//name如果name元素可能是孙子或更深层而不是直接子元素)那么你应该得到你期望的输出,即指定node的第一个name (分别是value )元素的value

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM