在XPath嵌套迭代中获取当前索引

时间:2014-12-13 16:24:41

标签: xml xslt xpath

我需要使用Xpath1.0在XML中迭代2个级别的嵌套循环。

迭代时,如果它是第二级的第一个元素,当前索引作为计数器,它应该打印元素值'Y'。

我能够完成其中一件事,但不能同时完成两件事。 例如如果我迭代第一个循环并使用position()它给我当前索引,但我无法识别它是否是第二级的第一个元素。类似地,如果我迭代第二个循环,我不会获得实际索引,但如果它是第一个元素使用position()= 1

我附上了样本请求和预期回复。

<root>
    <level1>
        <level2>name1</level2>
        <level2>name2</level2>
    </level1>
    <level1>
        <level2>name3</level2>
        <level2>name4</level2>
        <level2>name5</level2>
    </level1>
    <level1>
        <level2>name6</level2>
        <level2>name7</level2>
    </level1>
</root>

结果应该是这样的:

name1_Y_1
name2_N_2
name3_Y_3
name4_N_4
name5_N_5
name6_Y_6
name7_N_7

以下是现有代码:

<root>
    <xsl:for-each select="$Map-Data/root/level1/level2">
        <xsl:choose>
            <xsl:when test="position() = 1">
                <names>
                    <xsl:value-of select="concat(current(), '_Y_', position())"/>
                </names>
            </xsl:when>
            <xsl:otherwise>
                <names>
                    <xsl:value-of select="concat(current(), '_N_', position())"/>
                </names>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</root>

2 个答案:

答案 0 :(得分:1)

使用XPath 2.0,你可以做到

//level2/concat(., if (not(preceding-sibling::*)) then '_Y' else '_N', '_', position())

使用XPath 1.0时,您需要确保在迭代preceding-sibling::*时检查//level2并使用您的宿主语言进行字符串连接。

正如你现在展示的样本使用XSLT我会做

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

<xsl:output indent="yes"/>

<xsl:template match="root">
  <xsl:copy>
    <!-- <xsl:apply-templates select="$Map-Data/root/level1/level2"/> -->
    <xsl:apply-templates select="level1/level2"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="level2[not(preceding-sibling::*)]">
  <names>
    <xsl:value-of select="concat(., '_Y_', position())"/>
  </names>
</xsl:template>

<xsl:template match="level2[preceding-sibling::*]">
  <names>
    <xsl:value-of select="concat(., '_N_', position())"/>
  </names>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

您的方法不起作用的原因在于选择:

<xsl:for-each select="/root/level1/level2">

您选择所有 level2元素作为单个节点集 - 并且整个集合中只有一个成员可以位于#1位置。

OTOH,如果你要像这样分开你的选择:

<xsl:for-each select="/root/level1">
    <xsl:for-each select="level2">
        <xsl:choose>
            <xsl:when test="position() = 1">
                <names>
                    <xsl:value-of select="concat(current(), '_Y_', position())"/>
                </names>
            </xsl:when>
            <xsl:otherwise>
                <names>
                    <xsl:value-of select="concat(current(), '_N_', position())"/>
                </names>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</xsl:for-each>

您将为每个level1元素处理单独的节点集,从而实现在每个批次中位置#1处具有单独level2元素的目标。

但是,您不能使用相同的position()函数连续对所有元素进行编号,因为它会在每次level1更改时重新启动(这就是您使Y / N工作的方式)。为此,您必须使用其他编号机制,例如xsl:number

<xsl:for-each select="root/level1">
    <xsl:for-each select="level2">
        <names>
            <xsl:value-of select="."/>
            <xsl:choose>
                <xsl:when test="position() = 1">_Y_</xsl:when>
                <xsl:otherwise>_N_</xsl:otherwise>
            </xsl:choose>
            <xsl:number level="any"/>
        </names>
    </xsl:for-each>
</xsl:for-each>