【问题标题】:xslt and xpath: match preceding commentsxslt 和 xpath:匹配前面的注释
【发布时间】:2010-04-10 11:15:06
【问题描述】:

给定这个 xml:

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

我想使用 XPath 来选择所有前面有评论的项目节点,也就是说,我想选择“foo”和“another foo”项目,而不是“bar”项目。

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

【问题讨论】:

  • 请注意,在 XSLT/XPath 数据模型中,您正在寻找的那些项目元素不是紧跟在评论节点之前,而是在带有空格的文本节点之前。因此,除非您在样式表中使用 &lt;xsl:strip-space elements="*"/&gt;,否则这些空白文本节点可能会妨碍您。
  • 好问题 (+1)。请参阅我的答案以获得唯一(目前)完整的解决方案。 Martin Honnen 的 cmets 是正确的,尽管当前选择的答案可能对您有用,但它可能会突然停止这样做(如果 XPath 引擎提供了 XML 文档,其仅空白节点没有被剥离)。

标签: xslt xpath


【解决方案1】:

这似乎有效:

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

它寻找紧跟在 cmets 之后的兄弟姐妹,这些兄弟姐妹也是 &lt;item&gt; 元素。我不知道有更好的方式来表达::*[1]/self::item 部分,这很难看;请注意,如果它写成::item[1],那么它也会发现&lt;item&gt;s 不是立即由评论处理的。

【讨论】:

  • 如果评论和'item'元素之间有处理指令会发生什么?当然是边缘情况,但原始发布者首先需要澄清前面有处理指令前面有注释的“项目”元素是否是他想要选择的元素。
  • @Kevin:谢谢,凯文。这个 X-Path 对我来说很简单,而且效果很好:) @Martin:幸运的是我是 Xml 输入的主人,所以我确信不会有任何处理指令。无论如何感谢您的有用提示。
【解决方案2】:

当前选择的解决方案

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

【讨论】:

    【解决方案3】:

    this thread 中所述,引入一个测试(&lt;xsl:if test="..."&gt;&lt;/xsl:if&gt;),如:

    preceding-sibling::comment()

    只会测试节点是否有前面的兄弟节点是评论。

    如果你想知道,在前面的兄弟元素或 cmets 中,最近的一个是否是注释,你可以尝试:

    (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:生成的转换文件

    编辑:因为它没有考虑处理指令,所以我将该答案保留为 Community Wiki。

    【讨论】:

    • 感谢您对 VonC 的彻底回答!我尝试了“if”构造,它对我有用。不过,我宁愿使用 Kevin 的表达方式,因为这是我可以理解的(并希望记住)。
    • 这在 Martin Honnen 描述的情况下不起作用。
    • @Dimitre:您的回答 +1,我将我的保留为社区 Wiki。
    • 谢谢,你也很亲密。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 2013-05-23
    • 2021-09-02
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多