XSLT - 在嵌套的 for-each 中查找 position()

Posted

技术标签:

【中文标题】XSLT - 在嵌套的 for-each 中查找 position()【英文标题】:XSLT - find position() when inside a nested for-each 【发布时间】:2014-09-05 16:32:52 【问题描述】:

我正在使用 XSLT 对一些相当复杂的 XML 执行转换。为了实现我需要的输出,我必须创建一个嵌套的 for 循环,如下所示:

源 XML

<root>
  <element1>
    <child>
      <aaa></aaa>
      <bbb></bbb>
    </child>
  </element1>
  <element2/>
  <element3/>
  <element3/>
  <element3/>
</root>

XSLT

<xsl:for-each select="element3">
  <!-- Do some stuff -->

  <xsl:for-each select="../element1/child/*">
    <!-- Do some more stuff -->
  </xsl:for-each>
</xsl:for-each>

问题

我在这里尝试做的是在我的嵌套循环中(../element1/child/*) - 我想知道:

    我当前循环的元素的位置 - 例如,如果我当前专注于&lt;bbb&gt;,那么这个位置(我认为)将是“1” 父循环的位置(在element3 上) - 例如,如果我在&lt;element3&gt; 的第三个实例上,然后循环通过../element1/child/* 并专注于&lt;aaa&gt; - 这两个值我会在 '2' 和 '0' 之后。

理想情况下,我希望能够将这些值分配给一个变量。我试过使用下面的position() 表示法,但这似乎不起作用。

<xsl:for-each select="../element1/child/*">
  <xsl:variable name="postion_current_loop" select="position()"/>
  <xsl:variable name="postion_parent_loop" select="??????"/>
  <!-- Do some more stuff -->
</xsl:for-each>

如果有人对我如何实现这一目标有任何想法,将不胜感激!我正在使用 XSLT 2.0,但如果需要,我对使用 XSLT 1.0 的解决方案持开放态度。提前致谢。

【问题讨论】:

您是否有理由使用for-each 而不是单独的模板。如果您正确使用模板,我至少可以看到一个不错的解决方案。 我目前只使用一个模板 - 我对使用多个模板的解决方案持开放态度,因为我还没有弄清楚如何实现这一点。我对 XSLT 还很陌生,所以还没有完全掌握多个模板的概念。 好的。使用多个模板,这种事情会容易得多(XSLT 通常是 - 值得对这个概念有一个很好的理解)。 谢谢,我一定会探索的!目前,@michael.hor257k 提供的答案为我提供了解决方案的起点。 【参考方案1】:

您可以使用添加的@position 属性在内部重建节点,然后循环遍历重建树。对于这个 exsl:node-set(或其他专有派生类)是必需的。

获取您的原始 source.xml(未更改!)和此 XSLT 脚本:


<xsl:template match="/*">

<xsl:variable name="CopiedTree">
  <xsl:call-template name="copy-with-position"/>
</xsl:variable>

<xsl:for-each select="exsl:node-set($CopiedTree)//element3">
  <xsl:element name="E3">
    <xsl:comment>Do some stuff</xsl:comment>
    <xsl:variable name="E3pos" select="@position()" />

    <xsl:for-each select="../element1/child/*">
      <xsl:element name="E1C">
        <xsl:attribute name="name"><xsl:value-of select="name()" /></xsl:attribute>
        <xsl:attribute name="positions"><xsl:value-of select="concat($E3pos, ' - ', @position)" /></xsl:attribute>
        <xsl:comment>Do some more stuff</xsl:comment>
      </xsl:element>
      
    </xsl:for-each>
  </xsl:element>
</xsl:for-each>

</xsl:template>





    
<xsl:template name="copy-with-position">

  <xsl:element name="name()">
    <xsl:copy-of select="(@*)" />
    <xsl:attribute name="position"><xsl:value-of select="position()" /></xsl:attribute>
    <xsl:for-each select="./*">
      <xsl:call-template name="copy-with-position">
        <xsl:with-param name="position" select="position()" />
      </xsl:call-template>
    </xsl:for-each>
  </xsl:element>

</xsl:template>

输出是:

<?xml version="1.0" encoding="UTF-8"?>
<E3>
  <!--Do some stuff-->
  <E1C name="aaa" positions="3 - 1">
    <!--Do some more stuff-->
  </E1C>
  <E1C name="bbb" positions="3 - 2">
    <!--Do some more stuff-->
  </E1C>
</E3>
<E3>
  <!--Do some stuff-->
  <E1C name="aaa" positions="4 - 1">
    <!--Do some more stuff-->
  </E1C>
  <E1C name="bbb" positions="4 - 2">
    <!--Do some more stuff-->
  </E1C>
</E3>
<E3>
  <!--Do some stuff-->
  <E1C name="aaa" positions="5 - 1">
    <!--Do some more stuff-->
  </E1C>
  <E1C name="bbb" positions="5 - 2">
    <!--Do some more stuff-->
  </E1C>
</E3>

【讨论】:

【参考方案2】:

我在您的输入中添加了一些 id 属性,这样您就可以看到正在处理哪个节点,所以我的输入如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <element1 id="e1-1">
        <child>
            <aaa id="e1-a"></aaa>
            <bbb id="e1-b"></bbb>
        </child>
    </element1>
    <element2 id="e2-1"/>
    <element3 id="e3-1"/>
    <element3 id="e3-2"/>
    <element3 id="e3-3"/>
</root>

此样式表执行我认为您正在尝试执行的操作 - 它处理每个 element3 并处理每个 element3 的所有 ../element1/child/* 节点:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:output indent="yes"/>
    <xsl:template match="root">
        <tests>
            <xsl:apply-templates select="element3"/>
        </tests>
    </xsl:template>

    <xsl:template match="element3">
        <xsl:apply-templates select="../element1/child/*">
            <xsl:with-param name="parent-pos" select="position() - 1"/>
            <xsl:with-param name="parent-id" select="@id"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="element1/child/*">
        <xsl:param name="parent-pos"/>
        <xsl:param name="parent-id"/>
        <xsl:variable name="current-pos" select="position()"/>

        <test parent-id="$parent-id" current-id="@id" parent-pos="$parent-pos" current-pos="$current-pos"/>

    </xsl:template>

</xsl:stylesheet>

结果是:

<tests>
   <test parent-id="e3-1" current-id="e1-a" parent-pos="0" current-pos="1"/>
   <test parent-id="e3-1" current-id="e1-b" parent-pos="0" current-pos="2"/>
   <test parent-id="e3-2" current-id="e1-a" parent-pos="1" current-pos="1"/>
   <test parent-id="e3-2" current-id="e1-b" parent-pos="1" current-pos="2"/>
   <test parent-id="e3-3" current-id="e1-a" parent-pos="2" current-pos="1"/>
   <test parent-id="e3-3" current-id="e1-b" parent-pos="2" current-pos="2"/>
</tests>

希望有帮助

【讨论】:

【参考方案3】:

您应该尝试将输入修改为:

<root>
  <element1>
    <child>
      <aaa>1a</aaa>
      <bbb>2a</bbb>
    </child>
  </element1>
  <element2/>
  <element3>3a</element3>
  <element3>3b</element3>
  <element3>3c</element3>
</root>

然后看看你得到了什么:

<xsl:template match="/">
    <test>
        <xsl:for-each select="root/element3">
        <xsl:variable name="parent-position" select="position()" />
        <xsl:variable name="parent-value" select="." />
            <xsl:for-each select="../element1/child/*">
                <item value="." parent-value="$parent-value" parent-position="$parent-position" position="position()"/>
            </xsl:for-each>
        </xsl:for-each>    
    </test>
</xsl:template>

【讨论】:

以上是关于XSLT - 在嵌套的 for-each 中查找 position()的主要内容,如果未能解决你的问题,请参考以下文章

从多个for-each XSLT段输出HTML

如何在 XSLT 中打破 for-each 以及如何更新 XSLT 中的现有变量

xslt for-each 和排序问题

for-each 并使用 xslt 对 xml 进行排序和分组

XSLT for-each 无法提取值

在 msxsl 中将节点作为参数传递:来自 XSLT for-each 的脚本 javascript 函数不起作用