Compartilhar via


Mapping a Repeating Source to a Delimited List

My last couple of projects have been very interesting, challenging and a lot of fun.  My current project is no exception.  I have the pleasure of working with a great group of people (I've got to give a shout out to C-Mappity (Casey), v-slice (Dan), Sniff Packy Packet (Rich) and 10 Bit (pronounced 2 bit) (Mike) - you know who you are).  Anyways, this a great group and we are working on a fun project to create their services architecture which will be the framework for their ESB.

During our redesign and re-platform of  some of their current services we have come up on some interesting scenarios.  One that I think many people may run into is the need to take a source xml document that has a repeating list and map it to a destination that requires that repeating list to be formatted into a single delimited string.

The picture below shows what the Mapper looks like to get this working along with the structure of the source and destination schemas. 

If we take a look at a sample source xml we will get a better understanding of the format of the repeated list.

<SearchByCodeList>
    <FirstName>Jim</FirstName>
    <LastName>Anderson</LastName>
    <NetworkCodeList>
        <NetworkCode>44</NetworkCode>
        <NetworkCode>64</NetworkCode>
        <NetworkCode>49</NetworkCode>
</NetworkCodeList>
</SearchByCodeList>

and lets take a look at the output before we get into the mapping functionality

<ProviderSearchCanonical>
    <FirstName>Jim</FirstName>
    <LastName>Anderson</LastName>
    <NetworkCodeList>44|64|49</NetworkCodeList>
</ProviderSearchCanonical>

So, how do we do this in the Mapper?

We need to use the looping functoid and connect it to the NetworkCode node.  If our source xml had each NetworkCode node surrounded by opening and closing NetworkCodeList nodes then the looping functoid would need to be connected to the NetworkCodeList node.

Since we are using the looping functoid we will get an output record for each of the NetworkCode elements.  The first output would include the first value, the next loop would include the first value and the delimiter and the second value and so on.  This is not what we want.  We really want the last iteration of the loop to be output and none of the previous iterations.  This is what the script functoid at the bottom of the map does. 

The code returns a true when we want the output to be sent to the output message and false all other times.  The value of this functoid is sent to the Value Mapping functoid as the first parameter (which is the logical true or false that is required).  The code for the script functoid looks like:

public bool ShouldThereBeOutput( string inputIgnored, int totalRecords )
{
if(sbCount.Length == (totalRecords))
    {
return true;
    }
else
    {
return false;
    }
}

The totalRecs parameter is the value from the Record Count functoid (which is used in both Script functoids).  The inputIgnored parameter is a little trick.  We needed to make sure that the map processed the ShouldThereBeOutput method after the other Script functoid.  By drawing a line from the first Script functoid to this one we can ensure that it will be called afterwards.

Now lets look at the functionality in the first Script functoid.  The first thing that is in the code are two StringBuilder objects. They appear above the method declaration which will make them global in scope. 

System.Text.StringBuilder NCLState = null;
System.Text.StringBuilder sbCount = new System.Text.StringBuilder();

The first is our state object which will hold the delimited string.  The second is sbCount which holds the current iteration through the loop.  There is an Iteration functoid but that only keeps track of the iterations on the destination side.  We need the iteration that we are on based on the source.

The code below shows the method in the first Script functoid.  The bottom else statement is run for our first time through the loop.  It adds the value of the first node and appends a character to sbCount.  You can add any single character to sbCount.  The length property will tell us which loop we are currently in.  The embedded else statement is fired for every subsequent loop iteration and will prepend the delimiter.  Finally we check if the length of the sbCount object is equal to the value past in from the <<itermation>> functoid.  If it is then we are all done looping and need to return the final delimited string.  The code for this method looks like:

public string DetermineCodeList( string codeList, int totalRecords )
{
if(NCLState == null)
    {
NCLState = new System.Text.StringBuilder();
    }

if (sbCount.Length > 0)
if (sbCount.Length == totalRecords)
{return NCLState.ToString();}
else
        {
NCLState.Append("|" + codeList);
sbCount.Append("x");
return NCLState.ToString();
        }
else
    {
NCLState.Append(codeList);
sbCount.Append("x");
return NCLState.ToString();
    }
}

Once the map is executed we will end up with our delimited list.