XPath用于根据当前节点的内容选择父节点?

时间:2009-11-05 18:19:32

标签: xml xslt xpath

我有一个XML文档,其部分类似于以下内容:

<release_list>
  <release>
    <id>100</id>
    <file_list>
      <file>
        <id>20</id>
      </file>
      <file>
        <id>21</id>
      </file>
    </file_list>
  </release>
  <release>
    <id>101</id>
    <file_list>
      <file>
        <id>22</id>
      </file>
      <file>
        <id>21</id>
      </file>
    </file_list>
  </release>
  <release>
    <id>102</id>
    <file_list>
      <file>
        <id>22</id>
      </file>
      <file>
        <id>23</id>
      </file>
    </file_list>
  </release>
</release_list>

在某些时候,我有XSL for-each循环遍历每个版本,然后遍历每个文件列表。当我循环遍历每个文件时,我想获得有关哪些其他版本不包含相同文件ID的信息。 (例如,当我在版本100中迭代文件20时,我想获得指向发行版101和102的指针,但是当我在同一个包中迭代文件21时,我只想要一个指向发行版102的指针。)

有没有办法用XPath做到这一点?我最接近的是:

../../../release[ not( file_list/file/id = ./id ) ]

...当然失败了,因为在方括号内,'./'指的是括号前面的释放,而不是调用XPath语句的文件。

我在这里缺少什么?

3 个答案:

答案 0 :(得分:3)

在XSLT中这样的事情的一个好方法是在外层使用变量,然后你可以在选择路径中与它进行比较。例如,像

这样的东西
<xsl:variable name="id" select="id" />
<xsl:apply-templates select="/release_list/release[not(file_list/file/id = $id)]" />

可以帮到你。

答案 1 :(得分:3)

在谓词(方括号中的表达式)中,您可以使用the current() function获取当前节点(而不是.返回的上下文节点)。

但是,您应该将方法改为Peter Cooper Jr's answer。在XSLT中,最好在最外层进行过滤,以便只处理您需要的内容,而不是尝试处理所有内容,然后从内部强加排除。

同样值得理解的是,你根本没有使用循环; XSLT中的for-each是映射,而不是循环。换句话说,没有做第一个元素的概念,然后是第二个元素,然后是第三个元素......在概念意义上,所有匹配的元素都是同时处理的。这就是常见问题“如何在XSLT中摆脱循环?”的原因。只能用Matrix启发的词语来回答:“没有循环。”

答案 2 :(得分:0)

这是一个完整的解决方案,可以满足您的需求。当应用于您的示例输入XML时,以下XSLT 1.0样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="release">
    <xsl:copy>
      <xsl:copy-of select="id" />
      <xsl:apply-templates select="file_list/file" mode="show-other" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="file" mode="show-other">
    <xsl:copy>
      <xsl:copy-of select="id" />

      <!-- select all releases that do not contain any file with the same
           id as the current file -->
      <xsl:variable name="other" select="
        /release_list/release[not(current()/id = file_list/file/id)]
      " />
      <releases-without-that-file>
        <xsl:copy-of select="$other/id" />
      </releases-without-that-file>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

产生

<release>
  <id>100</id>
  <file>
    <id>20</id>
    <releases-without-that-file>
      <id>101</id>
      <id>102</id>
    </releases-without-that-file>
  </file>
  <file>
    <id>21</id>
    <releases-without-that-file>
      <id>102</id>
    </releases-without-that-file>
  </file>
</release>
<release>
  <id>101</id>
  <file>
    <id>22</id>
    <releases-without-that-file>
      <id>100</id>
    </releases-without-that-file>
  </file>
  <file>
    <id>21</id>
    <releases-without-that-file>
      <id>102</id>
    </releases-without-that-file>
  </file>
</release>
<release>
  <id>102</id>
  <file>
    <id>22</id>
    <releases-without-that-file>
      <id>100</id>
    </releases-without-that-file>
  </file>
  <file>
    <id>23</id>
    <releases-without-that-file>
      <id>100</id>
      <id>101</id>
    </releases-without-that-file>
  </file>
</release>

请注意

/release_list/release[not(current()/id = file_list/file/id)]

不同
/release_list/release[current()/id != file_list/file/id]

因为=与节点集中的任何节点进行比较(如果所有节点仅匹配,则返回true),而!=运算符与仅限节点集的第一个节点。

相关问题