Y之下的所有X元素,但在另一个之前,Y,Y

时间:2012-10-15 01:55:02

标签: xslt xpath xslt-2.0 xpath-2.0

<div n="a">
  . . . 
    . . .
      <spec>red</spec>
      <div n="d">
          . . . 
      </div>
      <spec>green</spec>
      . . .
         <div n="b">
            . . .
              <spec>blue</spec>
            . . .
         </div>
         <div n="c">
           <spec>yellow</spec>
         </div>
      . . .
    . . .
  . . .
</div>

[编辑删除了肖恩注意到的歧义。 - 谢谢]

当前元素是<div n="a">时,我需要一个XPATH表达式,它返回红色和绿色元素,而不是蓝色和黄色元素,如.//spec那样。

当前元素为<div n="b">时,同一表达式需要返回蓝色元素;当<div n="c">时,黄色元素。

.//spec[but no deeper than another div if there is one]

这样的东西

2 个答案:

答案 0 :(得分:3)

在XSLT 1.0中,假设当前节点是div

.//spec[generate-id(current())=generate-id(ancestor::div[1])]

在XSLT 2.0中,在相同的假设下

.//spec[ancestor::div[1] is current()]

纯XPath 2.0表达式

for $this in .
     return
        $this//spec[ancestor::div[1] is $this]

完整的XSLT 1.0转换

<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="div">
  <div n="{@n}"/>
  <xsl:copy-of select=
   ".//spec[generate-id(current())=generate-id(ancestor::div[1])]"/>
==============
  <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

应用于提供的XML文档

<div n="a">
  . . .
    . . .
      <spec>red</spec>
      <spec>green</spec>
      . . .
         <div n="b">
            . . .
              <spec>blue</spec>
            . . .
         </div>
         <div n="c">
           <spec>yellow</spec>
         </div>
      . . .
    . . .
  . . .
</div>

产生了想要的正确结果

<div n="a"/>
<spec>red</spec>
<spec>green</spec>
==============
  <div n="b"/>
<spec>blue</spec>
==============
  <div n="c"/>
<spec>yellow</spec>
==============

完整的XSLT 2.0转换

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

 <xsl:template match="div">

  <div n="{@n}"/>
  <xsl:sequence select=".//spec[ancestor::div[1] is current()]"/>
===================================
  <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

当应用于同一XML文档(上图)时,会产生相同的正确结果:

<div n="a"/>
<spec>red</spec>
<spec>green</spec>
===================================
  <div n="b"/>
<spec>blue</spec>
===================================
  <div n="c"/>
<spec>yellow</spec>
===================================

使用纯XPath 2.0(无current()):

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

 <xsl:template match="div">

  <div n="{@n}"/>
  <xsl:sequence select="
    for $this in .
     return
        $this//spec[ancestor::div[1] is $this]"/>
===================================
  <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

产生相同的正确结果

<div n="a"/>
<spec>red</spec>
<spec>green</spec>
===================================
  <div n="b"/>
<spec>blue</spec>
===================================
  <div n="c"/>
<spec>yellow</spec>
===================================

答案 1 :(得分:1)

假设您正在使用XSLT 1.0,并且您想要选择`spec'子项,所有子项,然后将您想要的“当前”节点作为XSLT焦点节点,请设置以下变量...

<xsl:variable name="divs" select="*//div" />

现在,您可以使用此XPath表达式选择所有spec后代之前没有div后代...

//spec[not((preceding::div|ancestor::div)[count(. | $divs) = count($divs)])]

买者

这应该可行,但我还没有测试过。谨慎行事,我将其作为练习留给OP进行测试。

注意

如果你真的非常想要一个不需要你声明一个额外变量的XPath表达式, AND 你碰巧很幸运,你已经在节点集中保存了当前节点(让称之为$ ref),你可以使用这个相当低效的XPath表达式......

$ref//spec[not((preceding::div|ancestor::div)[count(. | $ref/*//div) =
                                              count(    $ref/*//div)  ])]

附录

以下是我可能会引用评论流的测试用例。

测试案例1输入:

<div n="a">
      <spec>red</spec>
      <div n="x"/>
      <spec>green</spec>
      <div n="b">
        <spec>blue</spec>
      </div>
      <div n="c">
        <spec>yellow</spec>
      </div>
</div>

测试案例1预期输出:

  • 应该只是red