Saturday, February 11, 2006

BizTalk Mapper - Looping Tricks

This post is intended to demonstrate 2 ways of using the looping functoid. One is pretty obvious, and the other shows a use for the looping functoid that requires a bit of help from a logical functoid.

Along the way I'll also show how a value mapping functoid works.

I have created 2 schemas intended to represent the status of a previously sent purchase order. POS.xsd is the output from a Line of Business (LOB) application, and PO_Status is intended to be sent to the customer. The LineItem node in POS.xsd and the Product node in POStatus.xsd both repeat 1 to unbounded times. Here's how the map looks:

The red functoid is a String Concatenate functoid, which has 3 inputs - ShipperName, a constant " - ", and ServiceLevel. The DateShipped element is mapped straight across, or "as is".

The turquoise functoids are Greater Than (GT) functoids. The first input you can see on the screen for the upper GT functoid is the QuantityOrdered element, and the second input is "0". This functoid will return true if the value in QuantityOrdered is more than 0. The output of this functoid is mapped into 2 Value Mapping (VM) functoids. A value mapping functoid will return it's second input only if the value of the first input is 0. So the uppermost VM functoid will return its second input (the string "Ordered", which you can't see) if the value in QuantityOrdered is > 0, and that is mapped into GlobalQuantityTypeCode. The second VM functoid will return its second input (QuantityOrdered) if QuantityOrdered is > 0, and that value is mapped into ProductQuantity.

And Here's a sample of the input XML:

<ns0:PurchaseOrderStatus xmlns:ns0="http://Test.POS">
<ShipperName>UPS</ShipperName>
<ServiceLevel>2nd Day</ServiceLevel>
<DateShipped>05/17/2005</DateShipped>
<LineItem>
<ItemNumber>X-12</ItemNumber>
<QuantityOrdered>10</QuantityOrdered>
<QuantityShipped>5</QuantityShipped>
<QuantityBackordered>5</QuantityBackordered>
<QuantityCancelled>0</QuantityCancelled>
</LineItem>
<LineItem>
<ItemNumber>A-24-P</ItemNumber>
<QuantityOrdered>200</QuantityOrdered>
<QuantityShipped>200</QuantityShipped>
<QuantityBackordered>0</QuantityBackordered>
<QuantityCancelled>0</QuantityCancelled>
</LineItem>
<LineItem>
<ItemNumber>T-707F</ItemNumber>
<QuantityOrdered>12</QuantityOrdered>
<QuantityShipped>0</QuantityShipped>
<QuantityBackordered>6</QuantityBackordered>
<QuantityCancelled>6</QuantityCancelled>
</LineItem>
</ns0:PurchaseOrderStatus>

Notice in the Visual Studio output window above that there is an error. This occurred when I tried to test the map you see above. The warning immediately below the error reads in full: "The destination node "GlobalOrderQuantityTypeCode" has multiple inputs but none of its ancestors is connected to a looping functoid."

This error can be easily remedied by adding a looping functoid, which can be found in the Toolbox under Advanced Functoids:

In the image above, you can see that I have added a purple functoid (the Looping functoid) that connects the LineItem node in the source schema with the Product node in the destination schema. But there's still an error. What I've done in effect is to tell BizTalk, for every LineItem in node in a source document, create a Product node in the output document. But I'm going to have to also account for the fact that within 1 Product node in the output, there are possibly multiple OrderStatusQuantity nodes.

Here's another attempt:

I have put another Looping functoid on the map, and connected the QuantityOrdered and QuantityShipped node outputs to it. What does this do? Essentially, it says that whenever you have an input node, create another OrderStatusQuantity node. Well if I'm so clever, why am I still getting an error? Because other functoids that I've explained above say to only create the nodes under OrderStatusQuantity node if the source value is greater than 0. QuantityShipped in the 3rd LineItem in the sample XML is 0, so I'm essentially telling BizTalk to create an OrderStatusQuantity node, but don't create its children. Since the children are required in the schema, I get an error.

Let's try one more time:

Finally! If you look closely at the map, you can see that I've drawn lines from the GT functoids to the OrderStatusQuantity node. This shows a very useful property of logical functoids. If you map the output of a logical functoid to a node, it will act to suppress the production of that node when the output of the logical functoid is false. So the OrderStatusQuantity node will be created if and only if the QuantityOrdered or the QuantityShipped field contains a value greater than 0.

Here's the XML output. Note that in the last Product node, there is only one OrderStatusQuantity:

<ns0:PO_Status xmlns:ns0="http://Test.POStatusOut">
<ShipDate>05/17/2005</ShipDate>
<ShippedVia>UPS - 2nd Day</ShippedVia>
<Product>
<ProductIdentifier>X-12</ProductIdentifier>
<OrderStatusQuantity>
<GlobalOrderQuantityTypeCode>Ordered</GlobalOrderQuantityTypeCode>
<ProductQuantity>10</ProductQuantity>
</OrderStatusQuantity>
<OrderStatusQuantity>
<GlobalOrderQuantityTypeCode>Shipped</GlobalOrderQuantityTypeCode>
<ProductQuantity>5</ProductQuantity>
</OrderStatusQuantity>
</Product>
<Product>
<ProductIdentifier>A-24-P</ProductIdentifier>
<OrderStatusQuantity>
<GlobalOrderQuantityTypeCode>Ordered</GlobalOrderQuantityTypeCode>
<ProductQuantity>200</ProductQuantity>
</OrderStatusQuantity>
<OrderStatusQuantity>
<GlobalOrderQuantityTypeCode>Shipped</GlobalOrderQuantityTypeCode>
<ProductQuantity>200</ProductQuantity>
</OrderStatusQuantity>
</Product>
<Product>
<ProductIdentifier>T-707F</ProductIdentifier>
<OrderStatusQuantity>
<GlobalOrderQuantityTypeCode>Ordered</GlobalOrderQuantityTypeCode>
<ProductQuantity>12</ProductQuantity>
</OrderStatusQuantity>
</Product>
</nso:PO_Status>

6 comments:

Anonymous said...

Biztalk looping funtoid excellently explained,
thank You Steve

Jord said...

What anonymous said!

Clear progression from initial problem to final solution. I valued the intermediate "still not working" examples as much as the final solution.

Jay said...

Thanks! You wrote the magic sentences that made it all fall into place! "If you map the output of a logical functoid to a node, it will act to suppress the production of that node when the output of the logical functoid is false"

Udaya said...

Great explonation super

abc123 said...

Thank you... you saved my life

Steve Harclerode said...

abc123, your comment sounds a bit extreme, but I'm glad you found value.

:-)