2012-12-18 25 views
4

給出下面的XML(basic.xml):爲什麼javax.xml.xpath.XPath與克隆節點的行爲不同?

<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應用於第一個節點,或者也可能應用於原始文檔。我很喜歡克隆一個節點並使用它,但我真的很想知道爲什麼會發生這種情況。

回答

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元素可能是一個孫子或更深,而不是直接子),那麼你應該得到你所期望的輸出,即指定node的第一個name(分別爲value)元素子元素的值。

+0

謝謝你。我假設一個節點會丟棄它所屬的文檔;但這確實解釋了原因。 – Arthur