Mapping from flat list to a loop

June 20th, 2012

The problem solved by this map is moving flat data into a loop and, at the same time, adding qualifiers. In this case the source schema has three reference numbers elements under the same parent node. The target schema has one loop with two child elements -- a reference number qualifier element and a reference number element. You can see the schemas in the finished map below. NOTE: The Globals node was not present in the original schema.

The first problem we solve is producing three iterations of the target loop, one for each reference number. A looping functoid with three input links, one from each reference number will produce the desired three loops. CAUTION: The order of the input links is critical, as the order will determine which reference number appears in which loop.

Once we test and see that the correct number of loops is output (3), we then attach a link from each reference number to the ReferenceNumber node in the target. Testing the map again, we see that the map is outputting the three reference numbers in the correct order. CAUTION: Again the link order is critical, as the output order is determined by the link order.

Getting the reference number qualifiers in the correct order is more difficult since the qualifiers are not in the source data. Since we know the order of the reference numbers, though, we can manually control the qualifiers. Here's is what we do:

The first step is to add a node to the output schema, in this case the node Globals. Since we don't want the Globals node to be output as the application receiving the output file does not have this node in its schema, we must block the node from being created. To achieve this, we link an equal functoid to it. We will ensure that the equal functoid will never return true so that the Globals node is never created. One parameter of the equal functoid we set to "zzzzzQ". The other parameter comes from a scripting functoid that we add to the map. The scripting functoid contains the script:

public int flag = 0;
public void PutGlobals ()
{
}

Notice that the script return type is void, which means no value is output from the script -- thus "zzzzzQ" in the equal functoid will never be matched.

The script does a very simple thing. It creates a global int variable and sets the value to 0 (zero). In the BizTalk mapper, a global variable becomes available to all scripts in the map which are executed after the variable is declared. Why did we create the Globals node for this script rather than simply linking it to the LoopReferenceNumbers node? Because the mapping engine executes child nodes before it executes the parent in most cases. This is one of those cases. If the Globals script was linked to the LoopReferenceNumbers node then the variable would exist but not be accessible in the map.

The second step in outputting the qualifiers is to add a scripting functoid, linked to the ReferenceNumberQualifier node. This script in this functoid controls the order of output of the qualifiers. Here is the script:

public string PutRefNumbers ()
{
string retval = "C";
switch (flag)
{
case 0:
flag = 1;
retval = "A";
break;
case 1:
flag = 2;
retval = "B";
break;
default:
break;
}
return retval;

On the first pass through this script the value of flag is 0, thus the value "A" is output by the script. The script also sets the value of flag to 1 for the next pass through the script. On that next pass, the value "B" is output and the flag is set to 2. On the third pass through the script, neither of the case conditions are met, so the default return value of "C" is output.

Why did we set the values of flag instead of incrementing the value? Because the flow of the map depends on how the mapping engine generates the code. Sometimes, for example, the flow might generate 0, 1, 2, 3, etc. Sometimes it may generate 0, 2, 4, 6, as it will in this case. Why it does this is often hard to determine, so we usually follow the path of least resistance toward getting the desired output.

Here is the input file:

<ns0 :FlatReferenceNumbers>
<reference1>Reference A</reference1>
<reference2>Reference B</reference2>
<reference3>Reference C</reference3>
</ns0>

and here is the output file:

<ns0 :LoopReferenceNumbers>
<referencenumbers>
<referencenumberqualifier>A</referencenumberqualifier>
<referencenumber>Reference A</referencenumber>
</referencenumbers>
<referencenumbers>
<referencenumberqualifier>B</referencenumberqualifier>
<referencenumber>Reference B</referencenumber>
</referencenumbers>
<referencenumbers>
<referencenumberqualifier>C</referencenumberqualifier>
<referencenumber>Reference C</referencenumber>
</referencenumbers>
</ns0>

Oops, Bad Things Happen

May 3rd, 2012

Sorry, the blog crashed and burned and we'll have to repost everything.  I will do this as soon as possible -- right now I have my hands full.  Thanks for your patience.

Jim