BizTalk Mapper Patterns: How to Map Name Value Pair to a Hierarchical Schema

Posted: November 8, 2012  |  Categories: BizTalk Maps

In my last mapping pattern post, I explained how and described the pros and cons of four methods to accomplish a mapping from hierarchical schema to a name-value pair… so the logically next step would be to describe the inverse process.

When I decided to implement this mapping I really thought it would be extremely simple and almost not worth talking about… how I was wrong! It became really complicated, and exciting, as I tried to find a solution to optimize and improve the map.

As a developer, the first approach we think is to try to solve this mapping problem using only the available functoids, i.e. without custom XSLT. I quickly discarded this option and you will see why.

Note: Before I start the solution to this problem we need to notice that two elements are mapped directly:

  • “Id” to “NProcesso”
  • and “ServiceName” to “ServiceName”

First Solution: Using only functoids (without custom XSLT)

To solve this mapping problem using this approach, for each element in the destination schema we need to drag:

  • One Equal functoid and drag a link from the element “Name” in the source schema to this functoid, this will be the first condition in the functoid
    • And in the second condition, we need to put the element name of the destination schema that we try to map, for example, “Type”.
  • Drag a Value Mapping (Flattening) functoid to the grid
    • Drag a link from the Equal functoid to this Value Mapping (Flattening) functoid
    • Drag a link from the “Value” element in the source element to the Value Mapping (Flattening) functoid
    • And finally we need to drag a link from the Value Mapping (Flattening) functoid to the respective element in the destination schema, in this case, “Type” element as you can see in the picture below:
Name-value to hierarchical using only functoids
  • We need to repeat the above steps for all the element, with the exception of the “IPRoute” element, until we get the following map:
Name-value to hierarchical using only functoids
  • Because “IPRoute” element is a repeating element, we need to take a different approach:
    • We need to drag a Looping functoid and drag a link from “Property” record in the source schema to this functoid and then drag a link from the Looping functoid to the “IPRoute” element in the destination schema
    • Then we need to make the exact same steps described earlier (Equal functoid and Value Mapping (Flattening) functoid)
    • But because we want to create the “IPRoute” element ONLY if the name is equal to “IPRoute”, then we need to drag a link from the Equal functoid to the “IPRoute” element in the destination schema.
Name-value to hierarchical using only functoids

And this is it, it seems simple and it is!! However, this approach suffers from a serious problem that we can only detect by analyzing the XSLT regenerated by the BizTalk mapping engine: Performance!

If we analyze the XSLT regenerated by the BizTalk mapping engine by:

  • Right-click on the map and select the option “Validate Map”
  • In the Output windows, press CRTL key and click on the link of “The file in the output XSLT is stored in the following file”, it will open this file in a new window
  • Right-click and select “View Source” option

We will see that for each element in the destination schema it will be a one-for-each element:

<xsl:for-each select="Properties/Property>
 <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Name/text()) , 'Type')" />
  <xsl:if test="string($var:v1)='true'">
    <xsl:variable name="var:v2" select="Value/text()" />
    <Type>
      <xsl:value-of select="$var:v2" />
    </Type>
  </xsl:if>
</xsl:for-each>
<xsl:for-each select="Properties/Property">
  <xsl:variable name="var:v3" select="string(Name/text())" />
  <xsl:variable name="var:v4" select="userCSharp:LogicalEq($var:v3 , 'Protocol')" />
  <xsl:if test="string($var:v4)='true'">
    <xsl:variable name="var:v5" select="Value/text()" />
    <Protocol>
      <xsl:value-of select="$var:v5" />
    </Protocol>
  </xsl:if>
</xsl:for-each>

This means that if we have 50 occurrences of “Property” record, each filled with the elements Name and Value, we will have 50 iterations for each element that we want to map to the destination schema… in this scenario we have 12 elements, this means 600 iterations and will be worse if we are working with large maps or with high amounts of “Property” record occurrence.

Limitations of this approach:

  • Lack of performance
  • If the destination schema has many elements it takes to much work to do this kind of mapping and because we need many links and functoids to do this simple task it may become difficult to read the map.
  • If we add a new element in the destination schema, it requires that we have to rectify the mapping

Second Solution: Dynamic mapping using Inline XSLT

I soon realized that if I wanted a really good and effective solution, I would have to use custom XSLT.

And the second approach that I thought was trying to make a dynamic mapping, similarly to the third solution that I have accomplished in the “BizTalk Mapper Patterns: How to Map Hierarchical Schema to a Name-Value Pair” problem

To accomplish this, we need to:

  • Drag Scripting functoid to the map grid
    • In the scripting type select “Inline XSLT” option
    • In the Inline script put the following code:
<xsl:for-each select="/s0:Provisioning/Properties/Property">
 <xsl:if test="Name/text()!='IPRoute'">
    <xsl:element name="{normalize-space(*[local-name()='Name']/text())}">
      <xsl:value-of select="Value/text()"/>
    </xsl:element>
  </xsl:if>
</xsl:for-each>

Finally, drag a link from the Scripting functoid to the “Type” element in the destination schema:

Name-value to hierarchical Dynamic mapping using Inline XSLT

This looked like be my favorite approach because is completely dynamic. If another element was added to the destination schema I didn’t need to fix the mapping!

But unfortunately, this approach has several serious limitations.

Limitations of this approach:

  • The script only works well if all the elements contained in the “Properties” record are coming filled in the correct order of the elements in the destination schema.
  • Don’t work with nested records (or sub-records), if you notice in the script I ignore all “IPRoute” names

I could probably find other limitations but for me, these two are enough to discard this approach.

Third Solution: Using Inline XSLT along with XPath queries

After analyzing all the advantages and disadvantages, for me, this is the best (only) approach to accomplish this type of mapping problem. Again, because basically solves all limitations of previous solutions: it’s easy to create (only need basic knowledge of XSLT and XPath) and doesn’t have performance problems.

To accomplish this, we need to:

  • Replace the code of the Scripting functoid, existing in the previous solution, by:
<Type>
  <xsl:value-of select="//Properties/Property[Name='Type']/Value/text()" />
</Type>
<Protocol>
  <xsl:value-of select="//Properties/Property[Name='Protocol']/Value/text()" />
</Protocol>
<Pool>
  <xsl:value-of select="//Properties/Property[Name='Pool']/Value/text()" />
</Pool>
<VPNName>
  <xsl:value-of select="//Properties/Property[Name='VPNName']/Value/text()" />
</VPNName>
<IPAddress>
  <xsl:value-of select="//Properties/Property[Name='IPAddress']/Value/text()" />
</IPAddress>
<IPNetmask>
  <xsl:value-of select="//Properties/Property[Name='IPNetmask']/Value/text()" />
</IPNetmask>
<LAN>
  <xsl:for-each select="Properties/Property[Name='IPRoute']">
    <IPRoute>
      <xsl:value-of select="./Value/text()" />
    </IPRoute>
  </xsl:for-each>
</LAN>
<VirtualRouter>
  <xsl:value-of select="//Properties/Property[Name='VirtualRouter']/Value/text()" />
</VirtualRouter>
<IdleTimeout>
  <xsl:value-of select="//Properties/Property[Name='IdleTimeout']/Value/text()" />
</IdleTimeout>
<SessionTimeout>
  <xsl:value-of select="//Properties/Property[Name='SessionTimeout']/Value/text()" />
</SessionTimeout>
<TunnelType>
  <xsl:value-of select="//Properties/Property[Name='TunnelType']/Value/text()" />
</TunnelType>
Name-value to hierarchical Dynamic mapping using Inline XSLT

In this first approach, I tried to keep the code simple, but there is an important limitation in this code … I’m not validating the existence of optional fields. To do that we need to put the code a little more elaborate:

<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='Type']) /&gt; 0">
    <Type>
      <xsl:value-of select="//Properties/Property[Name='Type']/Value/text()" />
    </Type>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='Protocol']) /&gt; 0">
    <Protocol>
      <xsl:value-of select="//Properties/Property[Name='Protocol']/Value/text()" />
    </Protocol>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='Pool']) /&gt; 0">
    <Pool>
      <xsl:value-of select="//Properties/Property[Name='Pool']/Value/text()" />
    </Pool>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='VPNName']) /&gt; 0">
    <VPNName>
      <xsl:value-of select="//Properties/Property[Name='VPNName']/Value/text()" />
    </VPNName>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='IPAddress']) /&gt; 0">
    <IPAddress>
      <xsl:value-of select="//Properties/Property[Name='IPAddress']/Value/text()" />
    </IPAddress>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='IPNetmask']) /&gt; 0">
    <IPNetmask>
      <xsl:value-of select="//Properties/Property[Name='IPNetmask']/Value/text()" />
    </IPNetmask>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='IPRoute']) /&gt; 0">
    <LAN>
      <xsl:for-each select="Properties/Property[Name='IPRoute']">
        <IPRoute>
          <xsl:value-of select="./Value/text()" />
        </IPRoute>
      </xsl:for-each>
    </LAN>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='VirtualRouter']) /&gt; 0">
    <VirtualRouter>
      <xsl:value-of select="//Properties/Property[Name='VirtualRouter']/Value/text()" />
    </VirtualRouter>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='IdleTimeout']) /&gt; 0">
    <IdleTimeout>
      <xsl:value-of select="//Properties/Property[Name='IdleTimeout']/Value/text()" />
    </IdleTimeout>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='SessionTimeout']) /&gt; 0">
    <SessionTimeout>
      <xsl:value-of select="//Properties/Property[Name='SessionTimeout']/Value/text()" />
    </SessionTimeout>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//Properties/Property[Name='TunnelType']) /&gt; 0">
    <TunnelType>
      <xsl:value-of select="//Properties/Property[Name='TunnelType']/Value/text()" />
    </TunnelType>
  </xsl:when>
</xsl:choose>

Limitations of this approach:

  • Because we use scripting functoids we cannot read the entire map visually. We need to open the functoids and read, mainly, the XSLT code.
  • Need basic knowledge of XSLT and XPath
  • If we add a new element in the destination schema, it requires that we have to rectify the mapping

You can download the source code from:
BizTalk Mapper Patterns: Name-Value Transformation Pattern - How to Map Name Value Pair to a Hierarchical SchemaBizTalk Mapper Patterns: Name-Value Transformation Pattern – How to Map Name Value Pair to a Hierarchical Schema
GitHub

Author: Sandro Pereira

Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc. He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.

1 thought on “BizTalk Mapper Patterns: How to Map Name Value Pair to a Hierarchical Schema”

Leave a Reply

Your email address will not be published. Required fields are marked *

turbo360

Back to Top