Reputation: 2329
I have run into a problem with XSLT transformation. I have input XML as bellow:
<root>
<photoname>image</photoname>
<photocount>3</photocount>
</root>
and i need transform it to following XML:
<root>
<response>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_2_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_3_120.jpg</small>
</image>
</images>
</response>
</root>
Is it possble to do with XSLT(or with C# functions in XSLT)?
Upvotes: 3
Views: 2146
Reputation: 243549
Here is a non-recursive solution (you need to have enough nodes :) ), also known as the Piez method:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vCount" select="/*/photocount"/>
<xsl:template match="/*">
<root>
<response>
<xsl:for-each select=
"(document('')//node())[not(position() > $vCount)]">
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
</xsl:for-each>
</response>
</root>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<root>
<photoname>image</photoname>
<photocount>3</photocount>
</root>
the wanted, correct result is produced:
<root>
<response>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
</response>
</root>
Upvotes: 2
Reputation: 7662
There are probably many approaches to this problem. This is one of them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output indent="yes" />
<xsl:template match="root">
<xsl:element name="root">
<xsl:element name="response">
<xsl:call-template name="output">
<xsl:with-param name="name" select="photoname" />
<xsl:with-param name="value" select="photocount" />
</xsl:call-template>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template name="output">
<xsl:param name="name" />
<xsl:param name="value" as="xs:integer" />
<xsl:if test="$value > 1">
<xsl:call-template name="output">
<xsl:with-param name="name" select="$name" />
<xsl:with-param name="value" select="$value - 1" />
</xsl:call-template>
</xsl:if>
<xsl:element name="images">
<xsl:element name="image">
<xsl:element name="small">
<xsl:value-of select="concat($name, '_', $value, '_120.jpg')" />
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This is my output (under Altova XMLSpy):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<response>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_2_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_3_120.jpg</small>
</image>
</images>
</response>
</root>
Upvotes: 1
Reputation: 2045
Something like that:
<xsl:template match="photocount">
<xsl:call-template name="for.loop">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="for.loop">
<xsl:param name="i" />
<xsl:param name="count" />
<xsl:if test="$i <= $count">
<images>
<image>
<small>image_<xsl:value-of select="$i"/>_120.jpg</small>
</image>
</images>
</xsl:if>
<xsl:if test="$i <= $count">
<xsl:call-template name="for.loop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
Upvotes: 1
Reputation: 70638
You could achieve this in XSLT with a recursize template to iterate based on the photocount element
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<root>
<response>
<xsl:call-template name="photo">
<xsl:with-param name="name" select="photoname"/>
<xsl:with-param name="count" select="photocount"/>
</xsl:call-template>
</response>
</root>
</xsl:template>
<xsl:template name="photo">
<xsl:param name="name"/>
<xsl:param name="count"/>
<xsl:param name="current" select="1" />
<images>
<image>
<small>
<xsl:value-of select="concat($name, '_', $current, '_120.jpg')"/>
</small>
</image>
</images>
<xsl:if test="not($current >= $count)">
<xsl:call-template name="photo">
<xsl:with-param name="name" select="$name"/>
<xsl:with-param name="count" select="$count"/>
<xsl:with-param name="current" select="$current + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output:
<root>
<response>
<images>
<image>
<small>image_1_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_2_120.jpg</small>
</image>
</images>
<images>
<image>
<small>image_3_120.jpg</small>
</image>
</images>
</response>
</root>
Note that I didn't know how the _120 suffix was generated, so I had to hard-code it in the XSLT.
Upvotes: 1