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/*
) - 我想知道:
-
我当前循环的元素的位置 - 例如,如果我当前专注于
<bbb>
,那么这个位置(我认为)将是“1”
父循环的位置(在element3
上) - 例如,如果我在<element3>
的第三个实例上,然后循环通过../element1/child/*
并专注于<aaa>
- 这两个值我会在 '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()的主要内容,如果未能解决你的问题,请参考以下文章
如何在 XSLT 中打破 for-each 以及如何更新 XSLT 中的现有变量