In this new mapping pattern, I want to show you how can you transform a hierarchical schema into a Name/Value Pair record.
The first thing we need to know is how I can read the name of the element from the source schema. By default when we drag a link from the source to the destination schema, the value of the element is mapped to the destination schema, but we can change this behavior in the link properties by choosing “Copy name” in the “Source Links” property:
So to reach our goal we need to link to the source to the destination schema:
- The link to the “Name” element set with “Copy name” in the “Source Links” property
- And the link to the “Value” element set with “Copy text value” (the default value) in the “Source Links” property
Note: Before I start the solution to this problem we need to notice that two elements are mapped directly:
- “NProcesso” to “Id”
- and “ServiceName” to “ServiceName”
And one element is not mapped “Tipo_Operacao”. All other elements need to be mapped in the Name/Value Pair record.
First Solution: Using only Links
The first approach we think is to map all the elements using the method previously explained.
However, this approach has a problem because don’ the produce a valid message, instead of creating a record for each different element mapped he gathers all the Names and Values in a single record:
<ns0:Provisioning xmlns:ns0="http://SandroPereira.MappingToNameValueRecord.Provisioning"> <Id>Nprocesso_0</Id> <Properties> <Property> <Name>IPRoute</Name> <Name>Type</Name> <Name>Protocol</Name> <Name>Pool</Name> <Name>VPNName</Name> <Name>IPAddress</Name> <Name>IPNetmask</Name> <Name>Profile</Name> <Name>VirtualRouter</Name> <Name>IdleTimeout</Name> <Name>SessionTimeout</Name> <Name>TunnelType</Name> <Value>IPRoute_0</Value> <Value>Type_0</Value> <Value>Protocol_0</Value> <Value>Pool_0</Value> <Value>VPNName_0</Value> <Value>IPAddress_0</Value> <Value>IPNetmask_0</Value> <Value>Profile_0</Value> <Value>VirtualRouter_0</Value> <Value>IdleTimeout_0</Value> <Value>SessionTimeout_0</Value> <Value>TunnelType_0</Value> </Property> <ServiceName>ServiceName_0</ServiceName> </ns0:Provisioning>
To solve this problem we need to add a Lopping functoid and map all the elements in this functoid:
Limitations of this approach:
- If the source schema has many elements it takes much work to do this kind of mapping and because we need many links to do this simple task it may become difficult to read the map.
- We do not validate the existence of optional elements, which causes too many warnings
- A new element in the source schema requires that we have to rectify the mapping
Second Solution: Using only Links and validate the existence of optional elements
So to avoid warnings of optional elements in the first approach we need to use additional functoids. For each element mapped in the Name/Value Pair we need to drag:
- One Logical Existence functoid and drag a link from the source element to this functoid
- And two Value Mapping functoids:
- Drag a link from the Logical Existence functoid to the first Value Mapping functoid
- Drag a link from the Logical Existence functoid to the second Value Mapping functoid
- Drag a link from the source element to the first Value Mapping functoid and in the link properties set with “Copy name” the “Source Links” property
- Drag a link from the source element to the second Value Mapping functoid and in the link properties set with “Copy text value” the “Source Links” property
- Drag a link from the first Value Mapping functoid to the element “Name” in the destination schema
- Drag a link from the second Value Mapping functoid to the element “Value” in the destination schema
Limitations of this approach:
- If the source schema has many elements it takes 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.
- A new element in the source schema requires that we have to rectify the mapping
Third Solution: Using Inline XSLT
This is my favorite approach and why? Because basically solves all limitations of previous solutions: Too many tasks involved, too many links and functoids and most important it can be completely dynamic, i.e., if another element is added to the source schema I don’t need to fix the mapping!
So how can we accomplish this dynamic mapping?
We need to add to the grid two Scripting functoids:
- The first scripting functoid is to create a C# function to validate the existence of the element.
- In the scripting type select “Inline C#” option
- In the Inline script put the following code:
public string EmptyOrNull(string param) { if(string.IsNullOrEmpty(param)) return "false"; return "true"; }
- The second scripting functoid is for mapping all the element in the Name/Value Pair
- In the scripting type select “Inline XSLT” option
- In the Inline script put the following code:
<xsl:element name="Properties"> <xsl:for-each select="/s0:Request/Body/*"> <xsl:variable name="var:v2"> <xsl:value-of select="."/> </xsl:variable> <xsl:variable name="var:v1" select="userCSharp:EmptyOrNull(string($var:v2))" /> <xsl:if test="local-name()!='ServiceName' and local-name()!='LAN' and $var:v1='true'"> <xsl:element name="Property"> <xsl:element name="Name"> <xsl:value-of select="local-name()"/> </xsl:element > <xsl:element name="Value"> <xsl:value-of select="."/> </xsl:element > </xsl:element> </xsl:if> </xsl:for-each> <xsl:for-each select="/s0:Request/Body/LAN/*"> <xsl:variable name="var:v2"> <xsl:value-of select="."/> </xsl:variable> <xsl:variable name="var:v1" select="userCSharp:EmptyOrNull(string($var:v2))" /> <xsl:if test="$var:v1='true'"> <xsl:element name="Property"> <xsl:element name="Name"> <xsl:value-of select="local-name()"/> </xsl:element > <xsl:element name="Value"> <xsl:value-of select="."/> </xsl:element > </xsl:element> </xsl:if> </xsl:for-each> </xsl:element>
Finally, drag a link from the second scripting functoid to the “Properties# record in the destination schema:
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.
Fourth Solution: Using Table Looping functoid
Finally, I decided to create a fourth approach using the capabilities of the Table Looping functoid. For me, the big difference between this approach and the first two is that is probably to be easier to read.
So instead of using the looping functoid and drag all links from the source directly to the destination schema, we will drag the links all the links from the source schema to the Table Looping functoid… but because you cannot drag to link from the same element to the Table Looping functoid (To map the name and the value) you need to use a workaround:
- Drag a Table Looping functoid to the map grid
- For each element, drag a String Concatenate functoid to the map grid
- Drag a link from the source element to the String Concatenate functoid and in the link properties set with “Copy name” the “Source Links” property
- Drag a link from the String Concatenate functoid to the Table Looping functoid and in the link properties, set the “Label” property to the following text “<Element name> Name” (note: you need to change <Element name> to the element concerned, ex: “Type Name”)
- Drag a link from the source element to the Table Looping functoid and in the link properties set with “Copy text value” the “Source Links” property
In the Table Lopping functoid configuration we need to set the first to inputs:
- The first with the number of element mapped in the Table Lopping functoid
- The second with the number of columns, in this case, 2
And in the Table Lopping grid we need to manually associate the first column the name of the element and in the second the value of the element:
Finally, we need to drag to Table Extractor functoid and configure them to read the first and the second column of the table:
Limitations of this approach:
- If the source schema has many elements it takes 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.
- We do not validate the existence of optional elements, which causes too many warnings.
- A new element in the source schema 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 Hierarchical Schema to a Name Value Pair
GitHub
Very nice description of the various alternatives, Sandro. Thanks!
Great article. Just what I was looking for. Any advice on doing the reverse? Thanks
Hi Grant, this (the reverse) will be my next mapping post.