Reputation: 17
I'm rather new to XSLT and currently looking for a solution for my problem:
I would like to output a node <artnr><xsl:value-of select="sku" /></artnr>
n-times, where "n" is the value of another attribute <xsl:value-of select="qty" />
.
For example:
"sku" has the value 12345, "qty" has the value 3.
The output should look like this:
<products>
<artnr>12345</artnr>
<artnr>12345</artnr>
<artnr>12345</artnr>
</products>
So far I have prepared the following template, but it is not very efficient:
<products>
<xsl:for-each select="items/item">
<xsl:if test="qty = 1">
<artnr><xsl:value-of select="sku" /></artnr>
</xsl:if>
<xsl:if test="qty = 2">
<artnr><xsl:value-of select="sku" /></artnr>
<artnr><xsl:value-of select="sku" /></artnr>
</xsl:if>
<!-- etc. -->
</xsl:for-each>
</products>
To cover all realistic cases, this approach definitely isn't acceptable. This issue has been discussed here in similar cases a couple of times already, but I cannot make it work on my own example.
Thanks in advance!
Upvotes: 2
Views: 1041
Reputation: 52858
Here's an example of a recursive template if you're limited to 1.0.
Thanks for the sample XML input @Valdi_Bo :-)
Note: This assumes that qty
will be greater than zero. If a qty can be zero, you'll need to add a test before outputting artnr
.
XML Input
<items>
<item>
<qty>3</qty>
<sku>12345</sku>
</item>
<item>
<qty>2</qty>
<sku>23456</sku>
</item>
<item>
<qty>1</qty>
<sku>34567</sku>
</item>
</items>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="items">
<products>
<xsl:apply-templates/>
</products>
</xsl:template>
<xsl:template match="item">
<xsl:call-template name="output_sku"/>
</xsl:template>
<xsl:template name="output_sku">
<xsl:param name="qty" select="qty"/>
<xsl:variable name="remaining_qty" select="$qty - 1"/>
<artnr>
<xsl:value-of select="sku"/>
</artnr>
<xsl:if test="$remaining_qty >= 1">
<xsl:call-template name="output_sku">
<xsl:with-param name="qty" select="$remaining_qty"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output
<products>
<artnr>12345</artnr>
<artnr>12345</artnr>
<artnr>12345</artnr>
<artnr>23456</artnr>
<artnr>23456</artnr>
<artnr>34567</artnr>
</products>
Fiddle: http://xsltfiddle.liberty-development.net/bnnZVQ/1
Upvotes: 0
Reputation: 30991
In XSLT 2.0, among variants of xsl:for-each
loop there is
select="1 to <final_value>"
.
In your case this final_value is given by qty
element.
But one additional action is needed. Because inside xsl:for-each
the context
item is changed to the current element of the loop (in this case a number),
you have to:
item
element) in a variable
(I called it itm
),sku
child element.So the whole script can look like below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<products>
<xsl:for-each select="items/item">
<xsl:copy>
<xsl:variable name="itm" select="."/>
<xsl:for-each select="1 to qty">
<artnr><xsl:value-of select="$itm/sku" /></artnr>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</products>
</xsl:template>
</xsl:transform>
For a working example, with source XML, see: http://xsltransform.net/pNEhB36
If you are forced to use XSLT 1.0, you have to use a recursive template, calling itself again and again, up to the defined depth (one of its parameters).
I wrote the above example assuming that both qty
and sku
are
child elements. But now I realized that you actually wrote
that at least qty
is an attribute.
So item
element can be e.g.:
<item qty="2">
<sku>23456</sku>
</item>
Then instead of "plain" qty
you should write @qty
.
Upvotes: 1