In this new mapping pattern I want to show you how can you transform an 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 in 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 for 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 for this problem we need to notice that two element 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 mapped all the element using the method previously explained.
However this approach have 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’re 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 work, too many links and functoids and most important can be complete 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 for 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 ling 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: your 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 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’re 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
The sample code in Code Gallery as this workaround implemented.
BizTalk Mapper Patterns: How to Map Hierarchical Schema to a Name Value Pair (69.3 KB)
Microsoft TechNet Gallery
