XSLT和XPath:匹配前面的评论

问题描述:

给出这个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

这似乎工作:

//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

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

当前所选的溶液

//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