xslt和xpath:匹配前面的注释

时间:2010-04-10 11:15:06

标签: xslt xpath

给出这个xml:

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

我想使用XPath来选择所有在其前面有注释的项目节点,即我喜欢选择“foo”和“另一个foo”项,而不是“bar”项。

我已经摆弄了前兄弟轴和comment()函数,但无济于事。

3 个答案:

答案 0 :(得分:3)

这似乎有效:

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

它会立即查找同时也是<item>元素的评论兄弟。我不知道更好的方式表达::*[1]/self::item部分,这是丑陋的;请注意,如果它是::item[1],那么它也会发现<item>不会立即通过评论

答案 1 :(得分:3)

当前选择的解决方案

//comment()/following-sibling::*[1]/self::item
在评论和元素之间存在处理指令(或整组处理指令)的情况下,

不起作用 - 正如Martin Honnen在评论中所注意到的那样。 / p>

以下解决方案没有这样的问题

以下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"/>

答案 2 :(得分: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:生成的转化文件

编辑:由于它没有考虑处理说明,我把答案留给了社区维基。