Search code examples
xmlxsltxslt-1.0

How to explicitly count the number inside for-each loop in xslt 1.0?


I need to take count and increased one by one based on condition. position() function is not useful in below scenario,

Input XML:

<em>
    <doc>
        <info>
            <str>/abc/sdf</str>
            <str>/def/gjh</str>
            <str>/ghi/jhk</str>
            <str>/klm/jhhj</str>
            <str>/abc/hafghi</str>
            <str>/ghi/tyuxd</str>
            <str>/abc/ert</str>
            <str>/def/kjldg</str>
            <str>/abc/ghk</str>
            <str>/pqr/wsej</str>
            <str>/ghi/sdf</str>
            <str>/ghi/fgh</str>
            <str>/ghi/sfd</str>
            <str>/ghi/hgjgh</str>
            <str>/ghi/hfkkd</str>
        </info>
    </doc>
</em>

XSLT:

<req>
    <xsl:for-each select="/em/doc/info/str">
        <xsl:variable name="pos" select="position()"/>
        <xsl:if test="/em/doc/info/str[$pos] != '' and starts-with(/em/doc/info/str[$pos],'/abc/')">
            <input>
                <inf>
                    <xsl:valus-of select="/em/doc/info/str[$pos]"/>
                </inf>
            </input>
        </xsl:if>
    </xsl:for-each>
    <xsl:for-each select="/em/doc/info/str">
        <xsl:variable name="pos" select="position()"/>
        <xsl:if test="/em/doc/info/str[$pos] != '' and starts-with(/em/doc/info/str[$pos],'/ghi/')">
            <input>
                <inf>
                    <xsl:valus-of select="/em/doc/info/str[$pos]"/>
                </inf>
            </input>
        </xsl:if>
    </xsl:for-each>
</req>

Required Output:

<req>
    <input>
        <inf>/abc/sdf</inf>
    </input>
    <input>
        <inf>/abc/hafghi</inf>
    </input>
    <nxtinput>
        <inf>/ghi/jhk</inf>
    </nxtinput>
    <nxtinput>
        <inf>/ghi/tyuxd</inf>
    </nxtinput>
    <nxtinput>
        <inf>/ghi/sdf</inf>
    </nxtinput>
    <nxtinput>
        <inf>/ghi/fgh</inf>
    </nxtinput>
    <nxtinput>
        <inf>/ghi/sfd</inf>
    </nxtinput>
    <nxtinput>
        <inf>/ghi/hgjgh</inf>
    </nxtinput>
</req>

Condition:

1. If '/em/doc/info/str' starts with '/abc/' then will populate under '/input/inf' up to two times only even if its matched more than two times.
2. If '/em/doc/info/str' starts with '/ghi/' then will populate under '/nxtinput/inf' up to six times only even if its matched more than six times.

Above mentioned XSLT not return above mentioned output. it's return all matched values. i want to restrict to populate values under '/input/inf' and 'nxtinput/inf' until 2 and 6 times only.


Solution

  • You probably want to do:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/em">
        <req>
            <xsl:for-each select="doc/info/str[starts-with(., '/abc/')][position() &lt;= 2]">
                <input>
                    <inf>
                        <xsl:value-of select="."/>
                    </inf>
                </input>
            </xsl:for-each>
            <xsl:for-each select="doc/info/str[starts-with(., '/ghi/')][position() &lt;= 6]">
                <nxtinput>
                    <inf>
                        <xsl:value-of select="."/>
                    </inf>
                </nxtinput>
            </xsl:for-each>
        </req>
    </xsl:template>
    
    </xsl:stylesheet>
    

    This selects the nodes that meet the condition first, then applies a restriction on how many of these to output. But the result is different from the one you show, because there aren't enough nodes to fill the quota.