3

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)?

2
  • what if count is = '0'?? should <root><response><images> node be created ? Commented Feb 21, 2012 at 13:22
  • i can check it if count is null Commented Feb 21, 2012 at 14:21

4 Answers 4

2

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>
Sign up to request clarification or add additional context in comments.

1 Comment

@Miro: This isn't really a problem. You can always use: document('Hamlet.xml')//node() -- as I did in the past ... :)
1

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.

Comments

1

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 &lt;= $count">
    <images>
      <image>
        <small>image_<xsl:value-of select="$i"/>_120.jpg</small>              
      </image>
    </images>
  </xsl:if> 

  <xsl:if test="$i &lt;= $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> 

Comments

1

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 &gt; 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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.