2010-04-10 41 views
7

給出這個XML:XSLT和XPath:匹配前面的評論

<root> 
    <list> 
     <!-- foo's comment --> 
     <item name="foo" /> 
     <item name="bar" /> 
     <!-- another foo's comment --> 
     <item name="another foo" /> 
    </list> 
</root> 

我想使用XPath來選擇所有項目節點有評論緊接他們,這是我喜歡選擇「富」和「另一富」項目,但不是「酒吧」項目。

我已經弄亂了前面的兄弟軸和comment()函數,但無濟於事。

+0

注意,在XSLT/XPath數據模型的那些項目元素你正在尋找一個不是立即在註釋節點之前,而是在一個空白的文本節點之前。 因此,除非在樣式表中使用'',否則這些空白文本節點可能會妨礙您的使用。 – 2010-04-10 12:32:07

+0

好問題(+1)。請參閱我的答案,瞭解唯一(目前)完整的解決方案。 Martin Honnen的評論是正確的,儘管當前選定的答案可能適用於您,但它可能會突然停止這樣做(如果XPath引擎提供了XML文檔,其空白節點沒有被剝離)。 – 2010-04-10 16:08:13

回答

3

這似乎工作:

//comment()/following-sibling::*[1]/self::item 

它尋找緊隨其後的這也是<item>元素評論兄弟姐妹。我不知道一個更好的方式來表達::*[1]/self::item部分,這很醜陋;注意,如果它被寫入::item[1]那麼它也會發現<item> s不是立即通過評論繼續。

+0

如果在註釋和'item'元素之間有處理指令會發生什麼?當然,一個邊緣案例,但原始海報首先需要澄清一個'item'元素之前是否有註釋前面的處理指令是他想要選擇的元素。 – 2010-04-10 12:35:59

+0

@凱文:謝謝凱文。這個X-Path足夠簡單,讓我能夠掌握,而且它的工作原理非常完美:) @Martin:幸運的是,我是Xml輸入的主人,所以我確信不會有任何處理指令。無論如何感謝有用的提示。 – miasbeck 2010-04-10 13:52:14

+0

Martin Honnen的評論是正確的。 – 2010-04-10 16:09:39

0

this thread所提到的,將測試(<xsl:if test="..."></xsl:if>)像:

前同輩::評論()

那隻測試節點是否具有在前兄弟這是一個註釋。

如果你想知道,前面的兄弟姐妹是元素或意見,最近的一個是評論,你可以嘗試:

(preceding-sibling::*|preceding-sibling::comment())[1][self::comment()] # WRONG 

但是:這是行不通的,因爲雖然「[1]」在向後方向上的第一裝置用於 preceding-sibling這並不意味着,對於一個括號表達式 - 它 第一裝置,按文檔順序

你可以試試:

(preceding-sibling::*|preceding-sibling::comment())[last()][self::comment()] 

preceding-sibling::node()[self::*|self::comment()][1][self::comment()] 

例如:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:output omit-xml-declaration="no" indent="no"/> 

    <xsl:template match="//item"> 
    <xsl:if test="preceding-sibling::node()[self::*|self::comment()][1][self::comment()]"> 
     <xsl:value-of select="./@name" /> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

只會顯示:

foo 
another foo 

打字時:

C:\Prog\xslt\preceding-sibling_comment> 
    java -cp ..\saxonhe9-2-0-6j\saxon9he.jar net.sf.saxon.Transform -s:test.xml -xsl:t.xslt -o:res.xml 

有:

  • test.xml:您的文件顯示在你的問題
  • t.xslt:XSLT文件上面
  • res.xml:得到的變換文件

編輯:因爲它沒有考慮到處理指令,所以我把這個答案留作社區Wiki。

+0

感謝您的徹底解答VonC!我嘗試了「if」構造,它適用於我。我寧願使用凱文的表情,因爲這是我能理解的(並希望記住)。 – miasbeck 2010-04-10 13:54:48

+0

這在Martin Honnen描述的情況下不起作用。 – 2010-04-10 15:47:46

+1

@Dimitre:在你的回答中+1,我將以我的社區維基的形式離開。 – VonC 2010-04-10 16:07:24

3

當前所選的溶液

//comment()/following-sibling::*[1]/self::item 

不會在存在一個處理。第指令(或整組處理指令)的註釋和元件之間的情況下工作 - 正如Martin Honnen在評論中注意到的那樣。

下面的解決方案不存在這樣的問題

下面的XPath表達式選擇,它們或者立即由註釋節點之前,或者是緊接在空白僅文本節點,該節點立即被註釋節點之前先只元素節點:

(//comment() 
    /following-sibling::node() 
    [1] 
    [self::text() 
    and 
    not(normalize-space()) 
    ] 
     /following-sibling::node() 
      [1] [self::item] 
) 
| 
(//comment() 
    /following-sibling::node() 
    [1] 
    [self::item] 
) 

這是一個完整的測試

我們用這個XML文檔:

<root> 
    <list> 
     <!-- foo's comment --> 
     <item name="foo" /> 
     <item name="bar" /> 
     <!-- another foo's comment --> 
     <item name="another foo" /> 
     <!-- comment 3 --><item name="immed.after 3"/> 
     <!-- comment 4 --><?PI ?><item name="after PI"/> 
    </list> 
</root> 

當以下變換被施加在上面的XML文檔

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 


<xsl:template match="/"> 
    <xsl:copy-of select= 
    " 
    (//comment() 
     /following-sibling::node() 
     [1] 
     [self::text() 
     and 
     not(normalize-space()) 
     ] 
      /following-sibling::node() 
       [1] [self::item] 
    ) 
    | 
    (//comment() 
     /following-sibling::node() 
     [1] 
     [self::item] 
    ) 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

有用,正確的結果產生

<item name="foo"/> 
<item name="another foo"/> 
<item name="immed.after 3"/> 
+0

好,完整的答案。 +1 – VonC 2010-04-10 16:04:44