One of the most difficult tasks in the BizTalk Mapper is taking a flat list (such as a CSV or a flat SQL result) and grouping it into a header-detail hierarchy (such as an Invoice with multiple Lines).
Usually, developers resort to an external XSLT file, but this “kills” the visual mapper. There is an astonishing post by Chris Romp about Muenchian Grouping and Sorting in BizTalk Maps, but it has one limitation. By creating and configuring a Custom XSL Path, we lose all mapping features—you can no longer see your links or use functoids.
Here is how to use the Muenchian Method inside a Scripting Functoid to keep your map clean and functional.
📝 One-Minute Brief
Grouping flat structures into hierarchies is a common BizTalk challenge. While Custom XSLT is a solution, it often disables the visual mapper. This post demonstrates a “best of both worlds” approach: using a Scripting Functoid with Inline XSLT Call Template to implement Muenchian Grouping. This allows you to perform high-performance grouping and sorting while maintaining the ability to use standard functoids for the rest of your mapping logic.
So how can we use Muenchian Grouping without losing Map features?
My First approach: I was trying to put an Inline XSLT functoid and put all the XSL inside
<xsl:key name="groups" match="Order" use="OrderId"/>
<!-- This will loop through our key ("OrderId") -->
<xsl:for-each select="Order[generate-id(.)=generate-id(key('groups',OrderId))]">
<!-- And let's do some sorting for good measure... -->
<xsl:sort select="OrderId" order="ascending"/>
<Order>
<OrderId><xsl:value-of select="OrderId/text()" /></OrderId>
<Items>
<!-- Another loop... -->
<xsl:for-each select="key('groups',OrderId)">
<ItemId><xsl:value-of select="ItemId" /></ItemId>
</xsl:for-each>
</Items>
</Order>
</xsl:for-each>
The problem with that approach is that it gives an error:
- XSLT compile error at (9,8). See InnerException for details. ‘xsl:key’ cannot be a child of the ‘ns0:OutputOrder’ element
So, to avoid this error, we need to separate “<xsl:key name=”groups” match=”Order” use=”OrderId”/>” expression from the rest of the XSL (see the second approach)
Second Approach
Add two scripting functoids to the map>
- In the first, configure it to an Inline XSLT Call Template and put the key expression
<xsl:key name="groups" match="Order" use="OrderId"/>
- In the second, configure to an Inline XSLT and the rest of the XSL
<!-- This will loop through our key ("OrderId") -->
<xsl:for-each select="Order[generate-id(.)=generate-id(key('groups',OrderId))]">
<!-- And let's do some sorting for good measure... -->
<xsl:sort select="OrderId" order="ascending"/>
<Order>
<OrderId><xsl:value-of select="OrderId/text()" /></OrderId>
<Items>
<!-- Another loop... -->
<xsl:for-each select="key('groups',OrderId)">
<ItemId><xsl:value-of select="ItemId" /></ItemId>
</xsl:for-each>
</Items>
</Order>
</xsl:for-each>
See Sample 1, map: MapOrder.btm.
How can we improve (a little more) this solution
When leading with large files, speed processing is vital. Classical Muenchian grouping uses generate-id(). Muenchian grouping using generate-id() is slower than using the count() function, and shows the worst scalability. The reason is probably the poor implementation of the generate-id() function. In other words, the count() function performs much better.
So to improve Meunchian a little more, we have to use the count() function instead of generate-id():
<xsl:for-each select="Order[count(. | key('groups',OrderId)[1]) = 1]">
- See Sample 1, map: MapOrder2.btm.
Here are some performance stats that I found (see original post):
The graph view works better:
Don’t sacrifice the BizTalk Mapper’s visual benefits just because you need complex grouping. By leveraging the Muenchian Method inside a Scripting Functoid, you get high-performance transformations without the maintenance headache of external XSLT files.
Download
THIS SOLUTION IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND.
You can download Muenchian Grouping and Sorting in BizTalk Maps without losing Map functionalities from GitHub here:
Hope you find this helpful! If you liked the content or found it useful and would like to support me in writing more, consider buying (or helping to buy) a Star Wars Lego set for my son.





Both articles are lovely and usefull. I faced one issue when I configured what Chris suggested. Inside VS, testing map gives expected outcome. When I deployed the application, created a recieve location and port (configured map on receive port), also configured a send port which subscribes to the rport, droping the sampl file, I get an ouput file exactly the same as the input which is Order222|Item123Order111|Item456Order222|Item789as if map is not appliedAm I missing something, thankls for your help
Now I tried your maps inside my application configured Receive Port and Send port, outcome is the same, no xml is coming out, the same flat file is send to ourput directory. Did you apply your solution in a run time environment?Thanks for your help
Hi,Yes I apply the solution in run time environment.Both solutions, Chris Romp and mine, work well.Just deploy the schemas and map into a BizTalk application, and then: • Create new ReceivePort (one-way) ex: ReceivePortMG o New Receice Location: Type: FILE, Receive pipeline: XMLReceive o Inbound Map: config the map, In my case o Source: InputOrder o Map: MapOrder o Target: OutputOrder • Create SendPort (static one-way): Type: FILE, Send pipeline: XMLTransmit o Apply a filter to the send port o Property: BTS.ReceivePortName o Operator: == o Value: ReceivePortMGStart the ports.
Hi, check this solution: http://code.msdn.microsoft.com/Muenchian-Grouping-and-790347d2 have two folders:C:DevelopmentTestesMuenchianGroupingSamplesINC:DevelopmentTestesMuenchianGroupingSamplesOUT
http://social.msdn.microsoft.com/Forums/en-US/biztalkgeneral/thread/f88dd14f-5897-4e7a-b482-01cc14c62cf6
How about two level group? Let’s say, we have first level by Customer, then second level group by Order id. How can I do this?
Hi Walter,
You need to create a key like this:
You can see a sample here “How to implement multi-level Muenchian grouping in BizTalk Maps“: http://sandroaspbiztalkblog.wordpress.com/2012/05/15/biztalk-training-mapping-how-to-implement-multi-level-muenchian-grouping-in-biztalk-maps/