Sunday, 07 October 2007

Hi

A guy on the newsgroups recently needed to create exactly 5 elements in the output of his map, no matter how many records appeared in the input.

Well, I am always looking for new things to try out, and frankly, my XSLT coding skills could be better, so I thought I'd give it a shot.

I created a project with the following input schema:

The schema is for an XML document, and has a header (1..1), a LoopingRecord (1..1) and a footer (1..1). The LoopingRecord has a Field5 element, that can appear at most 5 times.

The output schema looks like this:

This schema is for a flat file. It baiscally has the same signature as the input schema - exception being that the Field1 element has minOccurs=5. It MUST be present 5 times - this is a schema for a positional file.

The map is pretty simple:

Header and footer are mapped using regular mapping techniques. But the Detail-element is created using a custom scripting functoid.

The string concatenate functoid only has one input, the string "5". This is because I want to create exactly 5 elements in the ouput.

The custom scripting functoid is an "Inline XSLT Call TEmplate" scripting functoid with the following code:

<xsl:template name="CreateXElements">
   <xsl:param name="totalCount" />
      <Detail>
         <xsl:for-each select="/*[local-name()='InputRoot']/*[local-name()='LoopingRecord']/*[local-name()='Field5']">
            <DetailLoop>
               <Field1><xsl:value-of select="text()" /></Field1>
            </DetailLoop>
         </xsl:for-each>
         <xsl:variable name="countRecords" select="count(/*[local-name()='InputRoot']/*[local-name()='LoopingRecord']/*[local-name()='Field5'])" />
         <xsl:if test="$countRecords&lt;$totalCount + 1">
            <xsl:call-template name="BuildTheRest">
               <xsl:with-param name="counter"><xsl:value-of select="$countRecords + 1" /></xsl:with-param>
               <xsl:with-param name="totalCount"><xsl:value-of select="$totalCount" /></xsl:with-param>
            </xsl:call-template>
         </xsl:if>
      </Detail>
</xsl:template>
<xsl:template name="BuildTheRest">
   <xsl:param name="counter" />
   <xsl:param name="totalCount" />
      <DetailLoop>
         <Field1></Field1>
      </DetailLoop>
      <xsl:if test="$counter&lt;$totalCount">
         <xsl:call-template name="BuildTheRest">
            <xsl:with-param name="newCounter"><xsl:value-of select="$counter + 1" /></xsl:with-param>
         </xsl:call-template>
      </xsl:if>
</xsl:template>

Basically, I start by copying existing nodes to the destination. As I have explained in this post you need to use XSLT for the whole thing. You can't copy the existing nodes using the mapper and create the new nodes using XSLT. After the existing nodes have been copied, I create the new empty nodes, by recursively calling a template that will create a single element for me.

You can find my entire project here: CreateXNumberOfElements.zip (23.33 KB)

I hope this will come in handy for someone in the future.

--
eliasen

Sunday, 07 October 2007 00:09:42 (Romance Daylight Time, UTC+02:00)  #    Comments [4]  | 

Theme design by Jelle Druyts