Quantcast
Channel: Mapping – SANDRO PEREIRA BIZTALK BLOG
Viewing all 36 articles
Browse latest View live

How BizTalk Maps Work – Processing model (Part 1)

$
0
0
Introduction

Maps or transformations are one of the most common components in the integration processes. They act as essential translators in the decoupling between the different systems to connect. This article aims to explain how maps are processed internally by the engine of the product as we explore the map editor BizTalk Server.

This series of post are based on the series “BizTalk Server – Basics principles of Maps” published earlier where it’s explained in detail the basic functionalities of maps and how they can be implemented. This article intends to be an introductory note for whom is taking the first steps in this technology.

As explained in the previous article, when we perform a transformation in the message there are 5 basic functionalities that typically arise:

  • Simple mapping of a given value (direct copy)
  • Concatenation of values
  • Conditional selection
  • Custom scripts
  • Add new values (data)

Based on these functionalities we will explain how the BizTalk Mapper Designer processes and translates these links internally. To better understand its functioning, we perform some changes to the structure of the destination schema: we add an optional element (“CivilStatus”) and intentionally disorganized its structure.

disorganized-destination-schema

Processing model of BizTalk Maps

Although traditionally the information is extracted from the source as it is being processed, in reality in the models based on XSLT what happens is exactly the opposite: each element in the target (destination schema) causes the search for the corresponding in the source. Here’s a traditional example:

  • The source is traveled (parsed) from beginning to end of file;
  • The information is extracted from the source in the exact order that is found;
  • The mapping rules are constructed as the source is traveled (parsed).

BizTalk also uses this technique in the conversions of Flat Files to XML format (Syntax Transformations, also explained in BizTalk Server – Basics principles of Maps), however the transformations inside maps use a different approach, as we will see later in this article.

One of the important factors when using integration tools is also having in mind the existing standards format or technologies , and that’s what the creators of the product made. BizTalk works internally, almost exclusively, with XML documents, so it made sense to use a standard format to carry out this type of transformations. For that W3C defined the XSLT (Extensible Stylesheet Language Transformation) as the standard format to represent the transformations between XML documents.

This way all links and functoids that are graphically visible in the Mapper Grid are not more than a graphical representation of an XSLT document that transforms the source document in a particular format specified by the destination schema, i.e., BizTalk maps are graphical representations of XSLT documents that allow us to perform, in a simple and visual manner, transformations between XML messages.

We can say that BizTalk maps always have its focus in the final document, thereby making sense that the mapping rules to be processed in the sequence required to create the final document. When the map is compiled, these rules are translated into XPath queries and XSLT functions in order to transform the required information, i.e., the mapping rules are built from the destination (target) and not by the source as some traditional tools (or traditional models).

BizTalk maps follows the model below:

  • The BizTalk mapping engine traverses the destination schema from beginning to end;
  • The mapping rules are constructed and executed as links are encountered in the destination schema;
  • The information is extracted from the source when a link is encountered in the destination schema.

 

Note: We can use an XSLT created by an external application and include it on the map inside a Scripting functoid (XSLT custom script) or by importing a XSLT file that carrying out the complete transformation of the map (of course the editor – BizTalk Mapper Designer – does not represent the graphical representations – links and functoid – in this scenario).

Related links



How BizTalk Maps Work – Deconstructing a map (Part 2)

$
0
0

In this article we will use the basic mapping operations previously described, and analyze the decisions taken by the BizTalk mapping engine.

Basically on this mapping problem there are two similar schemes, which we intend to map the source elements in their proper destination and for which we implemented the following challenges:

  • Concatenate the first and last name in order to give the full name (Concatenation of values);
  • Map the Address in its destination element (Direct copy)
  • Transform the date of birth in age (Custom scripts);
  • The Zip Code should only be mapped if it has a valid string, otherwise it will not be mapped (Conditional selection);
  • The element Civil Status is optional and as we have no evidence in the source to map it, it should be ignored (Add new values).
  • Additionally, we perform a more advanced mapping logic to calculate the total of national and international calls using cycles, conditional selections and mathematical operations.

As you can see, we intentionally switched the order of elements in the destination schema in order to, more easily, understand and verify how BizTalk maps works.

This way we obtained the following final map:

How-Maps-Work-final-result

Note: The order in which we perform the links between the elements from source to destination schema, in this scenario, is irrelevant to the compiler, however the same cannot be said for functoids. The functoids require certain input parameters that can vary according to the functoid that we are using, in this case the order with which we associate the link is extremely important

The previous image of the map is actually the following representation of this XSLT document:

Based on this document we will follow the behavior of the compiler. What he performs is to translate the links on the map, as he finds them, while he traverses the destination schema from beginning to end:

  • The first element found is “Address”; Since there is link associated to this element, the link is translated by one XPath expression (”Address/text()”) that defines the element to be extracted from the source:

<Address>
   <xsl:value-of select="Address/text()" />
</Address>
  • The second element found is “ZipCode” also with a link associated; It is a conditional selection that will be translated into a XSLT condition (xsl:if):

<xsl:variable name="var:v1" select="userCSharp:LogicalExistence(boolean(ZipCode))" />
<xsl:if test="string($var:v1)='true'">
   <xsl:variable name="var:v2" select="ZipCode/text()" />
   <ZipCode>
      <xsl:value-of select="$var:v2" />
   </ZipCode>
</xsl:if>
  • The third element is “CivilStatus”, since there are no links associated with this element, it is ignored in the mapping process.
  • The fourth element to be processed is “FullName” and once again there is a link associated, which corresponds to the concatenation of two elements of the origin; If you check the generated code, is assigned to the element “FullName” the value of variable “$var:v3” that is generated from the execution of the function userCSharp:StringConcat that visually represents the String Concatenate Functoid:

<xsl:variable name="var:v3" select="userCSharp:StringConcat(string(LastName/text()) , &quot;, &quot; , string(FirstName/text()))" />
<FullName>
   <xsl:value-of select="$var:v3" />
</FullName>
  • The fifth element is “Age”, a link is found which means that custom script found inside the CSharp Inline Scripting Functoid will be carried out:

<xsl:variable name="var:v4" select="userCSharp:CalculateMyAge(string(DateOfBirth/text()))" />
<Age>
   <xsl:value-of select="$var:v4" />
</Age>
  • Finally we found the record “PhoneBilling” that contain two elements: “TotalInternational” and “TotalNational” both with links associated.
    • Note that although we have not defined any cycle through loop functoid, the compiler is smart enough to realize that we are dealing with a cycle and translate it correctly.
    • However, unfortunately, it will create an own cycle for each element. For a better optimization, it will require us to use a custom script.
    • Both elements are calculated using conditions and mathematical calculations as you will see in the generated code

<PhoneBilling>
   <xsl:variable name="var:v5" select="userCSharp:InitCumulativeSum(0)" />
   <xsl:for-each select="/s0:PersonOrigin/PhoneCalls">
      <xsl:variable name="var:v6" select="userCSharp:StringLeft(string(@PhoneNumber) , &quot;4&quot;)" />
      <xsl:variable name="var:v7" select="userCSharp:LogicalEq(string($var:v6) , &quot;+351&quot;)" />
      <xsl:variable name="var:v8" select="userCSharp:LogicalNot(string($var:v7))" />
      <xsl:if test="string($var:v8)='true'">
         <xsl:variable name="var:v9" select="@Cost" />
         <xsl:variable name="var:v10" select="userCSharp:AddToCumulativeSum(0,string($var:v9),&quot;1000&quot;)" />
      </xsl:if>
   </xsl:for-each>
   <xsl:variable name="var:v11" select="userCSharp:GetCumulativeSum(0)" />
   <TotalInternational>
      <xsl:value-of select="$var:v11" />
   </TotalInternational>

   <xsl:variable name="var:v12" select="userCSharp:InitCumulativeSum(1)" />
   <xsl:for-each select="/s0:PersonOrigin/PhoneCalls">
      <xsl:variable name="var:v13" select="string(@PhoneNumber)" />
      <xsl:variable name="var:v14" select="userCSharp:StringLeft($var:v13 , &quot;4&quot;)" />
      <xsl:variable name="var:v15" select="userCSharp:LogicalEq(string($var:v14) , &quot;+351&quot;)" />
      <xsl:if test="string($var:v15)='true'">
         <xsl:variable name="var:v16" select="@Cost" />
         <xsl:variable name="var:v17" select="userCSharp:AddToCumulativeSum(1,string($var:v16),&quot;1000&quot;)" />
      </xsl:if>
   </xsl:for-each>
   <xsl:variable name="var:v18" select="userCSharp:GetCumulativeSum(1)" />
   <TotalNational>
      <xsl:value-of select="$var:v18" />
   </TotalNational>
</PhoneBilling>

Related links


How BizTalk Maps Work – The sequence of links (Part 3)

$
0
0

“The order in which we perform the links between the elements from source to destination has a huge impact in the final result”. This statement is true and false at the same time!

In fact the order with which we associate links (Drag & Drop) from the source to different destination elements is irrelevant, since the compiler, as previously explained, will process them in the correct order… Except if we have to associate several links to the same destination element or functoid. In these two last cases the order in which the link association takes place is extremely important and can lead to unexpected results.

Impact of the order of links in functoids

The functoids require certain input parameters that can vary according to the functoid that we are using, in this case the order with which we associate the link is extremely important, a practical example is the Value Mapping Functoid.

This functoid returns the value of the second parameter if the value of the first parameter is “true”. If the value of the first parameter is not “true”, the corresponding element or attribute in the output instance message is not created. Therefore, it is necessary to respect the order in which we associate the links:

  • The first parameter must be a value “true” or “false”, generally from the output of some other Logical functoid or from a variable Boolean field in the input instance message.
  • The second is the value that is output if parameter 1 is “true”. This value can be from a link from a node in the source schema that represents simple content, the output from another functoid, or a constant input parameter.

If we change the order in which the links are associated to the functoid, it will lead to mapping errors or unexpected results, according to the functoid used.

The link reorganization in functoids is very easy to accomplish, you just open the functoid detail (double click) and use the sort buttons.

reorganizar-functoids

Impact of the order of links in elements of the destination schema

If we change the order in which we associate the links on the same element in the destination schema we can also have an impact on the desired final result.

Unfortunately, when we associated different links in the same element, there is no way or place in the graphical editor where you can check the order of the association, for example, similarly to what happens with the functoids. The only way to verify the order in these cases is to inspect the XSLT generated code or testing the map.

A good example of this scenario is when we associated two different Scripting functoid, both with custom inline XSLT scripts to the same destination element or record, once again, changing the order of the link association may have unexpected results.

Impact-order-links-in-elements-destination-schema

In this example the first Scripting functoid contain the following XSLT code:

<xsl:for-each select="Client">
   <Person>
      <Name>
         <xsl:value-of select="Name/text()" />
      </Name>
      <Sex>
         <xsl:value-of select="Sex/text()" />
      </Sex>
   </Person>
</xsl:for-each>

This code performs the mapping of all existing elements in the record “Client” from the source schema to the elements on the record “Person” in the destination schema.

The second Scripting functoid contain an identical XSLT code:

<xsl:for-each select="Employee">
   <Person>
      <Name>
         <xsl:value-of select="Name/text()" />
      </Name>
      <Sex>
         <xsl:value-of select="Sex/text()" />
      </Sex>
   </Person>
</xsl:for-each>

but this time it will map all existing elements in the record “Employee” from the source schema to the elements on the record “Person” in the destination schema.

The expected result is to appear in the final document all clients in the record “Person” and then all employees. If we change the order in which we association the links in the record “Person”, we will see that the result he also will be changed.

We can validate the result of this scenario in the following links:

The rule of Link Sequence

In brief, the mapping engine, process the rules by going through the destination schema from the beginning to the end, processing the links in the order that he finds and in case of multiple links in a particular element or functoid, they are processed in the order in which they were associated.

This means that the links associated with the parent nodes (records) are processed before links associated with the children (elements inside the record).

A good example of this scenario is the use of conditions in the parent node (record) when we want to influence the outcome according with a particular condition. So…let’s find all the names of female clients. To do this we will create a map with the following settings:

The-exception-rule-sequence-links

  • Open the Toolbox window and drag the Looping Functoid onto the grid;
    • Drag a link from the record “Client” from the source schema to the Looping Functoid;
    • Drag a link from the Looping Functoid to the record “Person” in the destination schema;
  • Drag the Equal Functoid from the Toolbox window onto the grid;
    • Drag a link from the element “Sex” from the source schema to the Equal Functoid;
    • Drag a link from the Equal Functoid to the record “Person” in the destination schema;
    • Configured the Equal Functoid, by double click in the functoid, and edit the second condition with the value “F” to build a condition equivalent to Sex = “F”;

sex-condition

The Equal Functoid will return the value “True” if the first input, in this case, the element “Sex” is equal to the second input parameter, ie, “F”. Otherwise it will return the value “False”. What will lead to: the record “Client” is only mapped if the condition returns the value “True”.

If you look the code generated:

…
<ns0:PeopleTarget>
   <xsl:for-each select="Client">
      <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Sex/text()) , "F")" />
      <xsl:if test="$var:v1">
         <Person>
            <Name>
               <xsl:value-of select="Name/text()" />
            </Name>
            <Sex>
               <xsl:value-of select="Sex/text()" />
            </Sex>
         </Person>
      </xsl:if>
   </xsl:for-each>
</ns0:PeopleTarget>

We can check that the first action of the map, after the cycle that travels the various elements of the record is: get the value generated in the Equal Functoid, which is represented in the variable “v1″;

And the second action is to validate the condition (IF) with the value of the first operation (v1), ie, will test whether the value “v1″ is “True” or “False”. If the condition is true the code within the condition is executed, otherwise it will move to the next element without doing anything. Therefore thus we obtain the desired result:

<ns0:PeopleTarget xmlns:ns0="http://HowMapsWorks.PeopleTarget">
   <Person>
      <Name>Elsa Ligia</Name>
      <Sex>F</Sex>
   </Person>
</ns0:PeopleTarget>
The exception to the rule of Link Sequence: Process links out of order

However there is one important exception to this rule of Link Sequence, especially when using custom scripts in recursive records or elements.

Once again, a good example of this scenario is the use of custom scripts to increment counters. We can illustrate this scenario by adding two Scripting Functoids to the map:

  • The first containing the initialization and the function to increment the counter;
    int myCounter = 0;
    public void IncrementCounter()
    {
       myCounter += 1;
    }
    
  • The second getting the value of the counter
    public int ReturnCounter()
    {
       return myCounter;
    }
    

Note: This example will be associated with a loop, or with a recursive element.

The-exception-to-the-rule-of-Link-Sequence-Process-links-out of-order

We would expect that in the first cycle the result of the second script was the value “1″, in the second cycle we obtained the value “2″ and so on. However, if we test the map, we will see that the reality is different:

<ns0:PeopleTarget xmlns:ns0="http://HowMapsWorks.PeopleTarget">
   <Person><LineNumber>0</LineNumber></Person>
   <Person><LineNumber>1</LineNumber></Person>
   <Person><LineNumber>2</LineNumber></Person>
</ns0:PeopleTarget>

As we can see in the result above, the sequence in which the links are executed is:

  • Create the record “PeopleTarget”;
  • Creating child elements, and carry out the link rules associated with them:
    • Execute the function “ReturnCounter” that will return the value “0″ in the first iteration.
  • Execute the links associated with the parent node:
    • Execution of the function “IncrementCounter”

As we can validate by checking code produced by the map:

…
<ns0:PeopleTarget>
   <xsl:for-each select="Client">
      <Person>
         <xsl:variable name="var:v1" select="userCSharp:ReturnCounter()" />
         <LineNumber>
            <xsl:value-of select="$var:v1" />
         </LineNumber>
         <xsl:variable name="var:v2" select="userCSharp:IncrementCounter()" />
         <xsl:value-of select="$var:v2" />
      </Person>
   </xsl:for-each>
</ns0:PeopleTarget>

Of course we can change the existing code in the Scripting Functoids so we can get around to this behavior and thus obtain the desired result. However, this example serves to warn that in some scenarios, especially in the use of custom scripts in recursive records or elements, it is necessary to verify and validate the sequence in which the rules (links) are executed.

Conclusion

With this article, as we explore some of the common mappings scenarios, trying to dismantle the options that the BizTalk Map engine has taken to fulfill with the original intent of the visual map.

When you begin to explore the world of maps, there are two questions that should evaluate carefully

  • What is the best way to solve a problem: guaranteed there are several approaches to solve a common problem. Often deciding which the best way turns out to be the most difficult. Compile and analyze the code generated can be a good start to begin to understand the impact of certain options.
  • Incremental Testing: very often we are tempted to try to solve a mapping problem from start to finish and only then we test solution. Leave it to the end can make it extremely difficult to detect problems in complex mappings. Limit the scope of testing should be a continuous and incremental process during the creation of maps, tests must be carried out as soon as a significant block is completed.

I hope this kind of hacking can help you to understand the behavior and debugging techniques for this type of elementary problems.

Related links

BizTalk Server – Basics principles of Maps (318.0 KB)
Microsoft | MSDN Code Gallery


BizTalk Training – Mapping – How to implement multi-level Muenchian grouping in BizTalk Maps

$
0
0

I already talked about Muenchian Grouping in BizTalk Maps in the past:

Inspired by a question in BizTalk Server Forums: XSLT Mapping Question (Summing Values)… I decided to solved this problem and publish this sample to help this user… what can I say… I simply love mapping problems!!!

So how can we can we perform multi-level Muenchian grouping in BizTalk Maps and perform some mathematical operations on this group?

You can read more about the problem in the thread, nut basically the problem is: To sum all records and create a unique record for records having same account type and city!

Solution

multi-level-Muenchian-grouping-sample1

Add two scripting functoids to the map:

  • In the first, configure to an “Inline XSLT Call Template” and put key expression
    <xsl:key name="groups" match="Record" use="concat(City, '|', AccountType)"/>
    
  • In the second, configure to an “Inline XSLT” and the rest of the XSL
    <xsl:for-each select="Record[generate-id(.)=generate-id(key('groups',concat(City, '|', AccountType)))]">
       <Record>
          <xsl:variable name="city" select="City/text()" />
          <xsl:variable name="type" select="AccountType/text()" />
    
          <AccountType>
             <xsl:value-of select="$type" />
          </AccountType>
          <City>
             <xsl:value-of select="$city" />
          </City>
    
          <xsl:variable name="negativeTotal" select="sum(//Record[(City = $city) and (AccountType = $type) and (Sign = '-')]/Price)" />
          <xsl:variable name="positiveTotal" select="sum(//Record[(City = $city) and (AccountType = $type) and (Sign = '+')]/Price)" />
          <xsl:choose>
             <xsl:when test="$positiveTotal &gt; $negativeTotal">
                <Sign>+</Sign>
                <Price>
                   <xsl:value-of select="$positiveTotal - $negativeTotal" />
                </Price>
             </xsl:when>
             <xsl:otherwise>
                <Sign>-</Sign>
                <Price>
                   <xsl:value-of select="$negativeTotal - $positiveTotal" />
                </Price>
             </xsl:otherwise>
          </xsl:choose>
       </Record>
    </xsl:for-each>
    
  • Drag a link from the Second Scripting Functoid to the record “Record” in the destination schema;

How to implement multi-level Muenchian grouping in BizTalk Maps (34.9 KB)
Microsoft | MSDN Code Gallery


BizTalk Training – Mapping – Grouping elements from different messages in BizTalk Maps

$
0
0

Ok this will be my third mapping problem published and I can tell that it will not be the last…

First of all let me tell you that for me there is no perfect solution to solve a particular mapping problem, often there are several ways to solve the problem and how it’s solved always depends in the experience and knowledge that you have with the tool or/and with XSLT (of course sometimes there are some solutions better than other or with higher performance)

Today Nicolas asked if I could help him with this particular problem and after talking with him we decided to publish the solution here so that it can help others with similar problems.

PROBLEM

We want to combine data from two different messages into one, more specifically, we want to combine each user with his address and each user can have multiple addresses.

This is the transformation screenshot:

screenshot

Sample of Users Input message:

<ns0:Users xmlns:ns0="http://BizTalk_Server_GenerationFichiersReleve.test">
  <User>
    <UserID>1</UserID>
    <LastName>Nicolas</LastName>
    <FirstName>Brun</FirstName>
  </User>
   <User>
    <UserID>2</UserID>
    <LastName>Philippe</LastName>
    <FirstName>Picard</FirstName>
  </User>
</ns0:Users>

Sample of AddressesInput message:

<ns0:Addresses xmlns:ns0="http://BizTalk_Server_GenerationFichiersReleve.Test2">
  <Address>
    <UserID>1</UserID>
    <AddressLine1>245 rue françois 1er </AddressLine1>
    <PostCode>69001</PostCode>
    <Town>Lyon</Town>
  </Address>
  <Address>
    <UserID>2</UserID>
    <AddressLine1>18 avenue Gerard Majax</AddressLine1>
    <PostCode>75001</PostCode>
    <Town>Paris</Town>
  </Address>
  <Address>
    <UserID>2</UserID>
    <AddressLine1>450 rue Michou</AddressLine1>
    <PostCode>75005</PostCode>
    <Town>Paris</Town>
  </Address>
</ns0:Addresses>

Sample of the expected result:

<ns0:Informations xmlns:ns0="http://BizTalk_Server_GenerationFichiersReleve.Test3">
  <Users>
    <User>
      <UserID>1</UserID>
      <LastName>Nicolas</LastName>
      <FirstName>Brun</FirstName>
      <Addresses>
        <Address>
          <UserID>1</UserID>
          <AddressLine1>245 rue françois 1er </AddressLine1>
          <PostCode>69001</PostCode>
          <Town>Lyon</Town>
        </Address>
      </Addresses>
    </User>
    <User>
      <UserID>2</UserID>
      <LastName>Philippe</LastName>
      <FirstName>Picard</FirstName>
      <Addresses>
        <Address>
           <UserID>2</UserID>
           <AddressLine1>18 avec Gerard Majax</AddressLine1>
           <PostCode>75001</PostCode>
           <Town>Paris</Town>
        </Address>
        <Address>
           <UserID>2</UserID>
           <AddressLine1>450 rue Michou</AddressLine1>
           <PostCode>75005</PostCode>
           <Town>Paris</Town>
        </Address>
      </Addresses>
    </User>
  </Users>
</ns0:Informations>

As I said earlier, there are several ways to solve a particular mapping problem, this is the solution I like!

SOLUTION

grouping-solution

The first part is a normal drag-and-drop mapping problem however decided to create a template to map the second part of the message (addresses), using a scripting functoid with a inline XSLT call template with the following code:

<xsl:template name="AddressTemplate">
<xsl:param name='id' select='UserID'/>
  <Addresses>
    <xsl:for-each select="../../../InputMessagePart_1/s0:Addresses/Address[UserID=$id]">
      <Address>
        <UserID>
          <xsl:value-of select="UserID/text()"/>
        </UserID>
        <AddressLine1>
     	  <xsl:value-of select="AddressLine1/text()"/>
        </AddressLine1>
        <PostCode>
     	  <xsl:value-of select="PostCode/text()"/>
        </PostCode>
        <Town>
     	  <xsl:value-of select="Town/text()"/>
        </Town>
      </Address>
    </xsl:for-each>
  </Addresses>
</xsl:template>

A special thanks also to my new team member Rui Machado for is contributions to develop this particular solution.

Grouping elements from different messages in BizTalk Maps (359.7 KB)
Microsoft | MSDN Code Gallery


BizTalk Mapper Patterns: Calling an external assembly from Custom XSLT in BizTalk Server 2010

$
0
0

This topic is not new in my blog, I’ve already talked about this in the past: Calling an external assembly from Custom XSLT – Custom Extension XML (Grid Property) however, I decided to revisit this functionality within BizTalk Server 2010 and you will know why soon.

So is usual in complex maps to have scripting functoid with custom inline XSLT, and sometimes is useful to call custom .Net components directly from XSLT.

Create .Net Component

To illustrate this functionality, I decided to create a Class Library project: MapperExtensionsFunctions with a simple class where it is implemented a method to format the numbers in three decimal places:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace MapperExtensionsFunctions
{
    public class MappingFunctions
    {
        public MappingFunctions()
		{
		}

        public string ToDecimalPoint(string Input)
        {
            CultureInfo ciEN = new CultureInfo("en-US", false);

            double ConvertionDouble = double.Parse(Input, ciEN);
            string Output = string.Format("{0:0.000}", ConvertionDouble);
            return Output;
        }
    }
}
How can we extend BizTalk map to support this functionality?

You can add a custom extension xml file to your solution in order to declare the namespace and use a method from a .Net assembly from XSLT.

The extension file should look something like this:

<ExtensionObjects>
  <ExtensionObject Namespace="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" AssemblyName="MapperExtensionsFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cdbffba4cc751306" ClassName="MapperExtensionsFunctions.MappingFunctions" />
</ExtensionObjects>

Note: “http://schemas.microsoft.com/BizTalk/2003/ScriptNS0” is the “default namespace”, however you can change it (see more here)

In the properties of your BizTalk Mapper, use the Custom Extension XML property to open the Select Custom Extension XML File dialog box, in which you can select the file that contains the custom extension XML for the map (file above).

Custom-Extension-XML

Finally in your Inline XSLT functoid you can use the methods from the assembly by:

<xsl:variable name="result" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint(MarketPrice/text())" />

Or in this case, inside Inline XSLT Call Template:

<xsl:template name="PriceTemplate">
  <xsl:param name="market" />
  <xsl:param name="owner" />
  <xsl:element name="Price">
    <xsl:variable name="pmarket" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint($market)" />
    <xsl:variable name="powner" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint($owner)" />
    <PriceMarket>
      <xsl:value-of select="$pmarket" />
    </PriceMarket>
    <PriceOwner>
      <xsl:value-of select="$powner" />
    </PriceOwner>
  </xsl:element>
</xsl:template>
Houston we have a problem!

After Build, deploy and configure my project with success I decided to test my solution, but I keep getting the following error:

The Messaging Engine failed while executing the inbound map for the message coming from source URL:”C:\TEMP\PORTS\IN_CAR\*.xml” with the Message Type “http://BizTalk.CallingExternalAssemblyFromInlineXSLT.CarInfo#CarProperty&#8221;. Details:”Cannot find the script or external object that implements prefix ‘ScriptNS0′.”

At first glance, this error suggests that the assembly in question is not published in the GAC… However even after re-publish, the assembly in the GAC the problem remained!

Don’t panic, believe it or not, you did everything right… BizTalk Server 2010/Visual Studio 2010 have a bug (or issue): Visual Studio does not persist the path of Custom Extension XML property in the .BTM file.

So if someone wishes to use an external assembly in via a Inline XSLT/XSLT Template scripting functoid they cannot specify the external assembly through an extension xml file.

I tried to install the latest cumulative update package for BizTalk (CU5) and Visual Studio service pack but the issue still remain active (you can see more detail and vote to fix it here)

Workaround (unfortunately it’smanual)

You need to manually edit .BTM file to add this node between the elements </ScriptTypePrecedence> and <TreeValues>:

<CustomXSLT XsltPath="" ExtObjXmlPath=".\ExternalAssembly.xml" />

Note: Is not mandatory the CustomXSLT node stand between the elements </ScriptTypePrecedence> and <TreeValues> however this is normal behavior of the compiler

However this is a workaround that can cause many problems, especially maintaining this issue… but stay tuned, because in my next post I will explain how we can solve this problem in a better way.

Calling an external assembly from Custom XSLT in BizTalk Server 2010 Maps (65.9 KB)
Microsoft Code Gallery


BizTalk Mapper: Custom Extension XML property fixed with PowerShell script (BizTalk 2010)

$
0
0

In my last post I explained how to call an external assembly from Custom XSLT in BizTalk Server 2010 using the Custom Extension XML property of the maps.

I also describe that BizTalk Server 2010/Visual Studio 2010 have an issue with this functionality: Visual Studio doesn’t persist the path of Custom Extension XML property in the .BTM file.

As I explained earlier, the workaround that currently exists requires that the user edit manually and .BTM file to add this node between the elements </ScriptTypePrecedence> and <TreeValues>:

<CustomXSLT XsltPath="" ExtObjXmlPath=".\xslt\ExternalAssembly.xml" />

However this is a workaround that can cause many problems, especially maintaining this issue.

It may seem a small issue but imagine you are developing a project that needs this functionality, so you know there is an issue (perhaps after a few hours searching for the solution) and you apply this manual workaround. After solving the problem for the first time you may think that the problem is solved from now on… but is not true!

If for some reason you need to make any changes on the map, when we make a new build to the project you get the message: Build: 1 succeeded or up-to-date, 0 failed, 0 skipped; and if you check the map properties you will see the “Custom Extension XML” property correctly filled… however the compiler will remove this line again, so you need to constantly fix manually the .BTM file.

The only way we can see, through Visual Studio, if the problem is solved or not, is to validate the map:

Validate-Custom-Extension-XML

You will notice that in the Output window will be displayed two links:

  • The first for the output XSLT
  • The second to the Extension Object

If we click in the second, without implementing any workaround, it will show you the following output:

<ExtensionObjects />

And what really was supposed to appear the contents of our ExternalAssembly.xml file:

<?xml version="1.0" encoding="utf-16" ?>
<ExtensionObjects>
  <ExtensionObject Namespace="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" AssemblyName="MapperExtensionsFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cdbffba4cc751306" ClassName="MapperExtensionsFunctions.MappingFunctions" />
</ExtensionObjects>

Now imagine that within five months you need to make a further modification in the map or a new member of the team will take this project to make this change, do you will remember that you need to implement this workaround?

So I spend the last day thinking “outside the box” in order to get a more effective way to solve this problem… and I found it!

Workaround 2 (automatic)

My friend and coworker José Antonio Silva is always trying to convince me to use PowerShell more often in order to automate certain processes and this has made me wonder … maybe it will be a good approach using PowerShell here to solve this problem.

So I created this PowerShell script that associated with the Pre-build actions of Visual Studio project will automatically fix this issue for us:

$xml = New-Object XML
$xml.Load($args[0])

if (!$xml.mapsource.CustomXSLT)
{
	$newelement = $xml.CreateElement('CustomXSLT')
	$newelement.SetAttribute('XsltPath', '')
	$newelement.SetAttribute('ExtObjXmlPath', $args[1])

	$xml.mapsource.InsertAfter($newelement, $xml.mapsource.ScriptTypePrecedence)
	$xml.Save($args[0])
}

Note: the PowerShell script is available for download on the TechNet gallery here.

The script accepts two parameters:

  • The first parameter is the path to the map that we want to configure the custom extension XML;
  • The second is the path to the ExternalAssembly.xml file file that refers the namespace to the FullyQualifiedName (FQN) of the .NET assembly.

Then we need to configure Pre-build actions of Visual Studio project to run this script:

  • Copy the PowerShell script file: ExternalAssembluFix.ps1 to your project directory;
  • Right-click on BizTalk project name and select “Properties” option;
  • On the right three choose “Build Events” option:
    • Select the button “Edit Pre-Build …” and in the “Pre-Build event command line” windows type the following command:

Powershell -File "$(ProjectDir)ExternalAssemblyFix.ps1" "$(ProjectDir)<name_of_the_map>" "$(ProjectDir)ExternalAssembly.xml"
      • Note: you must replace <name_of_the_map> with the name of your map.

Powershell-Pre-Build-event-command-line

Now everytime we made a build to the project, the PowerShell script will check of the element CustomXSLT  is present in the map:

  • If not he will insert automatic this element
  • If the element is present, the script will not do anything.

Therefore we don’t have to worry about maintenance this issue, and in the future when the problem is fixed we also don’t have to worry about removing this workaround, everything will work fine.

PowerShell script from Visual Studio Pre-build event failing

If you try to implement this solution you may get the following error when trying to build the project:

File …\ExternalAssemblyFix.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see “get-help about_signing” for more details.

But there are no reasons for concern. You just need to learn a few little tricks for running Windows PowerShell scripts.

The Get-ExecutionPolicy cmdlet simply tells you which of the four execution policies (policies that determine which Windows PowerShell scripts, if any, will run on your computer) is currently in-force. The Windows PowerShell execution policies include the following:

  • Restricted – No scripts can be run. Windows PowerShell can be used only in interactive mode.
  • AllSigned – Only scripts signed by a trusted publisher can be run.
  • RemoteSigned – Downloaded scripts must be signed by a trusted publisher before they can be run.
  • Unrestricted – No restrictions; all Windows PowerShell scripts can be run.

So you need to set the execution policy to RemoteSigned:

Set-ExecutionPolicy RemoteSigned

However we must remember that we are using an x64-bit operating system, they have two PowerShell shortcuts:

  • Windows PowerShell(32).lnk (x86): %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
  • Windows PowerShell.lnk (x64): %SystemRoot%\sysWOW64\WindowsPowerShell\v1.0\powershell.exe

And Visual Studio is probably launching the x86 version of PowerShell, so I advise you to set the execution policy in both versions.

I hope you enjoy this solution and perhaps this will help you avoid many problems.
Calling an external assembly from Custom XSLT in BizTalk Server 2010 Maps (65.9 KB)
Microsoft Code Gallery

The sample code in Code Gallery as this workaround implemented.

BizTalk Mapper: Custom Extension XML property fix with PowerShell (BizTalk 2010) (1.0 KB)
Microsoft TechNet Gallery


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

$
0
0

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:

Copy-name-link-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

first-graft-mapping-name-value

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.

mapping-name-value-using-links-1

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:

mapping-name-value-using-links-2

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

mapping-name-value-using-links-handling-opcionals

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:

mapping-name-value-using-inline-xslt

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

Table-looping-functoid-configuration

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:

Table-looping-functoid-configuration-2

Finally we need to drag to Table Extractor functoid and configure them to read the first and the second column of the table:

mapping-name-value-using-table-lopping-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 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



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

$
0
0

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 for this problem we need to notice that two element 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 bellow:

Name-value-to-hierarchical-using-only-functoids

  • We need to repeat the above steps for all the element except the “IPRoute” element, until we get the following map:

Name-value-to-hierarchical-using-only-functoids-2

  • 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-3

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 in 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 windows
  • Right-click and select “View Source” option

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

<xsl:for-each select="Properties/Property">
  <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Name/text()) , &quot;Type&quot;)" />
  <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 , &quot;Protocol&quot;)" />
  <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 in 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 complete 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 work 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 don’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 validate 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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

The sample code is available for download in Code Gallery:.

BizTalk Mapper Patterns: How to Map Name Value Pair to a Hierarchical Schema (59.2 KB)
Microsoft Code Gallery


BizTalk Mapper Patterns: How to map values from a repeating node into a single node using conditions

$
0
0

Well, I decided to take a few minutes of my vacation to play a little with … BizTalk, renew some knowledge, answer a few emails and maybe try to answer some questions on the forums.

This exercise (or pattern) is actually from a question that I found on the forums: Reg BizTalk Mapping, which I found interesting.

So what’s the best way to map some values from a repeating node into a single node base in some conditions?

Note: you can find all mapping logic of this exercise in the forum, however briefly we have a repeating node “TimeSeries” and based on Path attribute value of “TimeSeries” node we will map on some elements of the output message:

  • If “Path” attribute == “1″ then assign the value of “TimedValue” to “Quantity”
  • If “Path” attribute == “2″ then assign the value of “TimedValue” to “NRJQuantity”
  • If “Path” attribute == “3″ then assign the value of “TimedValue” to “AvgCal”
  • If “Path” attribute == “4″ then assign the value of “TimedValue” to “AvgDens”
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 attribute “Path” in the source schema to this functoid, this will be the first input parameter in the functoid
    • And in the second parameter we need to put the number that we want to find, in this case: “1”.
  • Drag a Value Mapping functoid to the grid
    • Drag a link from the Equal functoid to this Value Mapping functoid
    • Drag a link from the “TimedValue” element in the source element to the Value Mapping functoid
  • Drag a link from the Equal functoid for the element in question in the destination schema, in this case “Quantity” element
  • And finally we need to drag a link from the Value Mapping functoid to the respective element in the destination schema, in this case again “Quantity” element as you can see in the picture bellow.

map-values-from-repeating-node-into-single-node-using-conditions-with-functoids

  • We need to repeat the above steps for all the element until we get the following map:

map-values-from-repeating-node-into-single-node-using-conditions-with-functoids-all

This solution is correct and in fact is what’s normally we found in this type of mapping problems however this is not the best option in terms of performance. If we analyze the XSLT regenerated by the BizTalk mapping engine by:

  • Right-click in 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 windows
  • Right-click and select “View Source” option

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

<ns0:Req>
  <xsl:for-each select="TimeSeries">
    <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(@Path) , &quot;1&quot;)" />
    <xsl:if test="$var:v1">
      <xsl:variable name="var:v2" select="string(@Path)" />
      <xsl:variable name="var:v3" select="userCSharp:LogicalEq($var:v2 , &quot;1&quot;)" />
      <xsl:if test="string($var:v3)='true'">
        <xsl:variable name="var:v4" select="TimedValues/TimedValue/text()" />
        <ns0:Quantity>
          <xsl:value-of select="$var:v4" />
        </ns0:Quantity>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>

  <xsl:for-each select="TimeSeries">
    <xsl:variable name="var:v5" select="string(@Path)" />
    <xsl:variable name="var:v6" select="userCSharp:LogicalEq($var:v5 , &quot;2&quot;)" />
    <xsl:if test="$var:v6">
      <xsl:if test="string($var:v6)='true'">
        <xsl:variable name="var:v7" select="TimedValues/TimedValue/text()" />
        <ns0:NRJQuantity>
          <xsl:value-of select="$var:v7" />
        </ns0:NRJQuantity>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>
....

This means that if we have 50 occurrences of “TimeSeries” node, we will have 50 iterations for each element that we want to map in the destination schema… so this approach may be easy to implement and somewhat acceptable in small messages is extremely disadvantageous for large message.

Limitations of this approach:

  • Lack of performance
Second Solution: Using Inline XSLT

In this second approach what we will do is take the XSLT code generated by the compiler and optimize it by removing all unnecessary cycles and put this code in a Scripting functoid.

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="TimeSeries">
  <xsl:if test="string(@Path) = '1' ">
    <Quantity>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:if>
  <xsl:if test="string(@Path) = '2' ">
    <NRJQuantity>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </NRJQuantity>
  </xsl:if>
  <xsl:if test="string(@Path) = '3' ">
    <AvgCal>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </AvgCal>
  </xsl:if>
  <xsl:if test="string(@Path) = '4' ">
    <AvgDens>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </AvgDens>
  </xsl:if>
</xsl:for-each>
  • Finally drag a link from the Scripting functoid to one element in the destination schema, for example “NRJQuantity”

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting

Limitations of this approach:

  • May have some lack of performance if we work with large message because some unnecessary iterations in the cycle, however it is far more efficient than the first solution.
  • Some warnings saying that some required field has no incoming link.
  • Because we use scripting functoids we cannot read the entire map visually. We need to open the functoids and read, mainly, the XSLT code.
Third Solution: Using Inline XSLT along with XPath queries

After analyzing all the advantages and disadvantages, I decided that I could optimize even more the XSLT script in order to have a better performance but to do this I would have to use a different approach than the one that was used by the BizTalk mapper engine, and for me this is the best approach to accomplish this type of mapping problem, because basically solves all limitations of previous solutions: it’s easy to create (only need basic knowledge of XSLT and XPath) and don’t have performance problems.

To accomplish this, we need to:

  • Replace the code of the Scripting functoid, existing in the previous solution, by:

<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='1']) > 0">
    <Quantity>
      <xsl:value-of select="//TimeSeries[@Path='1']/TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='2']) > 0">
    <NRJQuantity>
      <xsl:value-of select="//TimeSeries[@Path='2']/TimedValues/TimedValue/text()" />
    </NRJQuantity>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='3']) > 0">
    <AvgCal>
      <xsl:value-of select="//TimeSeries[@Path='3']/TimedValues/TimedValue/text()" />
    </AvgCal>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='4']) > 0">
    <AvgDens>
      <xsl:value-of select="//TimeSeries[@Path='4']/TimedValues/TimedValue/text()" />
    </AvgDens>
  </xsl:when>
</xsl:choose>

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting-2

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
  • Some warnings saying that some required field has no incoming link.
Fourth Solution: Using Inline XSLT along with XPath queries (avoiding warnings)

So to avoid warnings saying that some required field has no incoming link we must split the XSLT code that we use in the last solution (Third Solution) in different blocks for each element in the destination schema

To accomplish this, we need to:

  • Drag four Scripting functoid to the map grid and drag a link from each Scripting functoid to each element in the destination schema
  • For each Scripting functoid:
    • In the scripting type select “Inline XSLT” option
    • In the Inline script put the code that corresponding to the element in the destination element, for example in the first:

<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='1']) > 0">
    <Quantity>
      <xsl:value-of select="//TimeSeries[@Path='1']/TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:when>
</xsl:choose>

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting-3

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

The sample code is available for download in Code Gallery:.

How to map values from a repeating node into a single node using conditions (142.6 KB)
Microsoft Code Gallery


BizTalk Mapper Patterns: Automatically Link The Record Elements By Structure, Name or Using Mass Copy

$
0
0

Exchanging or routing messages between existing applications, systems or external partners is one of the many common scenarios in Enterprise integration solutions. And sometimes they are build base on the same data model, however because they are built in different systems and teams they can have slight small differences like: the order of the elements, the name of the elements, the structure or even the namespace. As a result of that we need to be able to transformed the original message to the expect message by that systems.

BizTalk Mapper provides you with just-in-time assistance, through a shortcut menu, when you create links between two record elements of source and destination schemas:

  • You can create multiple links simultaneously if:
    • Relevant schema structures must be the same (node names are different but order is the same or similar;
    • Or Record, elements or field names must match;
  • You can manually create single links by dragging and dropping the source element to the destination element, if the destination schema don’t have a similar structures or the same names;
  • Or you can use Mass Copy Functoid to copy the element in the input instance message to a destination schema that use the <any> Element (some kind of generic schema). This functoid also copies any and all of its substructure, and re-creates it in the output instance message at the linked node in the destination schema.

The BizTalk Mapper provides you with just-in-time assistance, through a shortcut menu, when you create links between two record elements of source and destination schemas.

So for accomplish that, open your map and:

  • Drag-and-drop the root name of the source schema, in this case “PurchaseOrder” to the root name of the destination schema, which will be in this case “PO”, and release the direct button of the mouse.

just-in-time-assistance-BizTalk-Mapper

  • A windows assistance will pop up were you can select create record-to-record links automatically in the following ways:
    • Direct Link: Using this technique, the BizTalk Mapper links the record from source schema to the selected record in the destination schema. This will not copy any kind of hierarchy structure from the source to the destination, it will only link the record or node.
    • Link by Structure: Using this technique, the BizTalk Mapper attempts to match the Record and Field nodes within the Record nodes being linked according to the structures of those Record nodes, regardless of names of the corresponding nodes within those structures.
    • Link By Name: Using this technique, the BizTalk Mapper attempts to match the Record and Field nodes within the Record nodes being linked according to the names of the corresponding nodes, regardless of their structure, within the Record nodes being linked.
    • Mass Copy: The Mass Copy functoid enables your maps to use schemas that include any and anyAttribute elements. For information about the functoids available in BizTalk Mapper.

Our scenario will have a Purchase Order that will need to be mapped to 3 different systems with 3 different target schemas.

  • The System A have a similar schema of our system, with the same structure and the same names, but they are presented in a different order;
    • Because the destination schema have the same names of the source schema, the best approach in this scenario is to use the Link By Name option
  • The System B have also a similar schema of our system, we as the same order and the same schema structure, however the elements have different names;
    • Because the destination schema have the structure of the source schema, the best approach in this scenario is to use the Link By Structure option
  • The System C have a canonical schema (or generic schema) that will accept any structure.
    • Because the destination schema will accept any kind of structure, the best approach in this scenario is to use the Mass Copy option
Scenario A: How to link the record elements by name

To accomplished that we need:

  • Drag the mouse from the root name “PurchaseOrder” in the source schema, and then drop it to root name “PO” in the destination schema.
  • On the shortcut menu, click “Link by Name” option. The BizTalk Mapper will attempt to match all the Record, Elements and Field nodes under the “PurchaseOrder” node according to their names, regardless of their structure, within the node “PO”.

And the BizTalk Mapper will automatically map all the elements from the source schema to the target schema that have the same name, as the picture bellow will show:

Link-by-Name

Of course this technique as some limitations, the elements need to have the exact same name, and will only work in some scenarios.

Scenario B: How to link the record elements by structure

To accomplished that we need:

  • Drag the mouse from the root name “PurchaseOrder” in the source schema, and then drop it to root name “ExternalPO” in the destination schema.
  • On the shortcut menu, click “Link by Structure” option. The BizTalk Mapper will attempt to match all the Record, Elements and Field nodes under the “PurchaseOrder” node according to the structure of those elements, , regardless of names of the corresponding nodes within those structures, within the node “ExternalPO”.

And the BizTalk Mapper will automatically map all the elements from the source schema to the target schema that have the same structure, as the picture bellow will show:

Link-by-Structure

Again this technique have the same limitations of the previous one.

Scenario C: How to link using a mass copy functoid

To accomplished that we need:

  • Drag-and-drop the Mass Copy functoid from the Toolbox to the map grid page and then:
    • Link the source root name “PurchaseOrder” as input parameter for the Mass Copy functoid;
    • Link the Mass Copy functoid to the destination root name “CanonicalPurchaseOrder” to set the output parameter from the Mass Copy functoid;

Mass-Copy

The only problem in using the Mass Copy functoid is that it will move all the elements and values from the source schema to the destination schema but it also will include the targetNamespace of source schema in the destination schema and this behavior can be a problem in some scenarios.

Example of the map output:

<ns0:CanonicalPurchaseOrder xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.CanonicalPurchaseOrder">
 <From xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">From_0</From>
 <To xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">To_0</To>
 <LineItems xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">
 <Item>
 <Product>Product_0</Product>
 <Description>Description_0</Description>
 <Price>10</Price>
 <Quantity>10</Quantity>
 </Item>
 </LineItems>
</ns0:CanonicalPurchaseOrder>

So how can we avoid this behavior?

However sometimes the destination schema expect a different message, without the namespaces in the element:

<CanonicalPurchaseOrder xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.CanonicalPurchaseOrder">
  <From>From_0</From>
  <To>To_0</To>
  <LineItems>
     <Items>
        <Product>Product_0</Product>
        <Description>Description_0</Description>
        <Price>10</Price>
        <Quantity>10</Quantity>
     </Items>
  </LineItems>
</CanonicalPurchaseOrder>

I already saw this scenario many times. So how can we accomplish this?

There is no easy way to accomplished this without the need to use custom XSLT and you will need to customize the syntax to your scenario.

In this sample if we need to map everything without namespaces we need to “replace” the Mass Copy functoid for a Scripting functoid:

  • Drag-and-drop the Scripting functoid from the Toolbox to the map grid page and then:
    • And Link the Scripting functoid to the destination root name “CanonicalPurchaseOrder” to set the output parameter from the Scripting functoid;
    • This functoid will not have any input parameters;
  • Double-click in the Scripting functoid to show the Properties window, then:
    • Select the “Script Functoid Configure” tab, and then choose “Inline XSLT” as your selected script type
    • In the “Inline script” text box enter the following XSLT code without the comments (they are only for explaining the operation of the script):
<CanonicalPurchaseOrder>
      <xsl:for-each select="/s0:PurchaseOrder/*">
        <xsl:if test="local-name()!='LineItems'">
          <xsl:element name="{local-name(.)}">
            <xsl:value-of select="." />
          </xsl:element>
        </xsl:if>
      </xsl:for-each>

      <xsl:choose>
        <xsl:when test="count(/s0:PurchaseOrder/LineItems) &gt; 0">
          <LineItems>
            <xsl:for-each select="/s0:PurchaseOrder/LineItems/*">
              <Items>
                <xsl:for-each select="./*">
                  <xsl:element name="{local-name(.)}">
                    <xsl:value-of select="." />
                  </xsl:element>
                </xsl:for-each>
              </Items>
            </xsl:for-each>
          </LineItems>
        </xsl:when>
      </xsl:choose>
</CanonicalPurchaseOrder>

Basically what we are doing is:

  • The Scripting functoid will not add the root name, so we need to manually add it
  • The we are Getting all the element inside “PurchaseOrder” but because we need to travel all the elements inside the LineItems we need to treat this different, so we will get all the element except the ” LineItems”
  • The next step is check if there is “LineItems”
  • If exist we will travel all the element inside the LineItems (for each)
  • Because the “LineItems” only contain one record we will add this manually but we need an additional cycle this time to travel all the elements inside the record Item

And with that we create our own custom Mass copy Functoid

The sample code is available for download in Code Gallery:.

Automatically Link The Record Elements By Structure, Name or Using Mass Copy (148.5 KB)
Microsoft Code Gallery


BizTalk Server: Demystifying the Test Map operation in Visual Studio

$
0
0

In the past I wrote a series of post on Basics principles of Maps where I stated that testing should be a continuous process as you build your map, not only at the end of development, but when necessary or when an important mapping block is complete.

And the good thing is that this feature is available to developers in an easy manner and directly from our favorite development tool – Visual Studio – without the need to build and deploy the maps or even create and configure ports and test them in runtime.

Note that Visual Studio allows you also to Validate and Debug maps at design time.

For this we need to:

  • Open the Solution Explorer windows
  • And execute the test by right-clicking the map name and selecting “Test Map” option
  • Verify the results in the Output window.

By default an instance of the input schema is generated automatically with dummy values, according to their type, and tested in the selected map. At the end, the generated result or the errors that occurred are displayed in the output window.

01-Visual-Studio-Output-Window

However this scenario provably is not the ideal one and what we want is to test the map with an existing and real message and not a dummy one. And the good news is that, again, BizTalk Mapper allows us to specify a source document.

In addition, it also supports instances as native (CSV, EDI …) or Extensible Markup Language (XML) as input or output according to the scenarios.

For this we only need to configure the properties of the map before we execute the test:

  • Right-clicking the map name and select Properties option;
  • In the Properties window set “TestMap Input Instance” property with the path to the input instance message file to be used while testing the map.

02-Visual-Studio-Map-Properties-Windows

Other important properties that you can specify are:

  • TestMap Output Instance: Specifies the location where the Test Map should generate the output message.
  • TestMap Input: Specifies the format of the input instance message.
  • TestMap Output: Specifies the format for the output instance message.
  • Validate TestMap Input: Specifies whether you want to validate input instance messages against the source schema before you test the map.
  • Validate TestMap Output: Specifies whether you want to validate output instance messages against the destination schema after you test the map.

This last option, “Validate TestMap Output”, of the map properties is extremely important for the partial tests of maps. By setting this property as "False", allows us to test an incomplete map without being shown errors due lack of mandatory data, many of them associated with areas still to map.

This is nothing new, despite we often forget some of these properties. However I find myself constantly hear that, despite being easy to test one-to-one maps inside Visual Studio at design time, when we have multiple inputs to the map, many-to-one or many-to-many transformations, the same doesn’t happen, and it’s impossible to test this type of maps inside Visual Studio at design time because we only can specify an input message… and that it’s not true!

How can we test many-to-one or many-to-many maps inside Visual Studio?

Testing one-to-one maps is extremely simple and straightforward, however, many-to-one or many-to-many maps, despite being simple to test, it will required some additional steps.

First you have to understand that, Visual Studio doesn’t let us define multiple native instances in the “TestMap Input Instance” property, it only allows one instance message. But when a map has multiple source schemas, they are wrapped in a hierarchy multi-part message created by BizTalk.

So the only way for us to test this type of maps is to provide with an instance of the message in the “TestMap Input Instance” property that matched with the hierarchy multi-part message created by BizTalk.

The question here is how we can easily create an instance of multi-part message?

And the question is simple, by using the default behavior of the Test Map operation to make this work for us!

Let’s explain better, when we create a new map the “TestMap Input” and “TestMap Input Instance” proprieties are defined by default as: “Generate Instance” and “” (empty) respectively:

03-Visual-Studio-Map-Default-Properties-Windows

This means, as I explained earlier that if we test the map without changing this properties, one instance message will be generated automatically with dummy values. However in this scenarios, it will generate a dummy instance of the multi-part message expected by the map! AWESOME!

So what we need to do is:

  • Right-clicking the map name and selecting “Test Map” option
  • In the Output window the BizTalk Mapper will provide a link to the test’s output XML file and also to the input XML file.

04-Visual-Studio-Output-Window-input-and-output-file

  • Through the File Explorer, go to the directory folder specified folder in the Output window to have access to the dummy input file:
    • In this case: “C:\Users\Administrator\AppData\Local\Temp\1”
  • Copy the file to your project folder, in this case: “inputfile.xml”

This is a sample of this file:

05-multi-part-message-input-sample

As you can see, all the different input messages are there, with the write namespaces and so on… So what we need to do now is to:

  • Edit this file and put the User and Address messages that you want (you can do this easily with notepad)
  • And then specify the “TestMap Input Instance” property with this file.

As said earlier … we need some additional steps … but it is extremely easy to accomplish!


BizTalk Mapper: Best Practices when using Custom XSLT files

$
0
0

If you follow my blog or already saw one of my presentations in BizTalk innovation Days events about BizTalk Mapper Patterns and Best Practices, you already notice that I avoid using Custom XSLT files for solving a mapping problem.

I only recommend using custom XSLT files:

  • If you are dealing with huge message and High Performance is one of the primary requirements (and even then this maybe can be avoid)
  • Or if you already have a custom XSLT file provided by another team, or from another system that you can re-use in your map. In this case don’t try to reinvent the wheel!

But again, this is my personal opinion. You may argument that XSLT automatically generated code by the BizTalk mapper complier cannot perform as well as the personal Custom-XSLT code! And yes, I agree with you… in most of the cases… but it also depends on the approach that you are implementing to solve the problem.

the real secret is to find the best of both worlds: BizTalk Mapper and custom XSLT together. I can spend many hours discussing this topic, and this will also be addressed in my upcoming eBook in more detail, however I let you with two statements:

  • In most scenarios you wouldn’t really notice too much difference between using the BizTalk Mapper and a custom XSLT file;
  • I can prove to you that using properly the BizTalk Mapper can have the same performance that a custom XSLT file! (of course that can always be exceptions)

However if you are using an external XSLT file at least you should follow some of this best practices:

TIP 1: Always Add your custom XSLT files to the solution

You should always add the External custom XSLT files to your solution, so that they can be easily find and can be added to the source control.

TIP 2: Give it a proper name

Don’t call it “ExternalXSLT” or “CustomXSLTCode”, give it a proper name to the custom XSLT file that could identify the map.

  • For example: “<name of the map>-XSLTFile.xsl”

custom-xslt-file-proper-name

TIP 3: Add Standard XML Comments

You should add standard XML comments: “<!– Comment –>”, inside the custom XSLT code. As many time you think that it is important to explain little parts of the process/code.

...
<xsl:template match="/s0:Order">
   <xsl:variable name="var:v1" select="userCSharp:GetCurrentDateTime()" />
   <ns0:SAPOrder>
      <OrderId>
         <xsl:value-of select="Orderheader/OrderNumber/text()" />
      </OrderId>
      <ClientId>
        <!-- This is a dummy value, in real scenarios we probably get this value
        from an external system or DB base on the client name-->
        <xsl:text>1</xsl:text>
      </ClientId>
      <Dates>
   …
TIP 4: Rename the page grid

If you are using an external XSLT file, at least rename the default grid page (Page 1) to a name that draws attention to this fact, for example: “ThisMapUseAnExternalXSLTFile”. At least this way new BizTalk developer can easily read and understand that this maps uses an external XSLT file.

Rename-the-page-grid-custom-XSLT-file

Note: The best part in being a speaker at BizTalk events, is that we can discuss with the attendees and get feedback. I really love this part! And this last tip was given by Mikael Sand while we were discussing this topic, probably while we were taking a beer in Norway :)

Feel free to comment or add more tips to implement best practices.

The sample code is available for download in Code Gallery:.

BizTalk Mapper: External Custom XSLT file vs BizTalk Mapper (Best Practices) (69.5 KB)
Microsoft Code Gallery


How to migrate BizTalk maps to Windows Azure BizTalk Services (WABS) maps

$
0
0

Unlike schemas that you will be able to directly take your BizTalk Server schemas and use them in you WABS solution, BizTalk Maps needs to be migrated because despite the Transform Designer (mapping tool used for WABS) and the BizTalk Mapper Designer looks mostly the same in terms of layout and user experience, they are quite different and the underlying map format is different. Basically when we are using BizTalk Mapper Designer the transformation are created using XSLT, however with Transform Designer the transformation are created using Extensible Application Markup Language (XAML).

Also the available functoids, that are now called Operations, are different as well. Not all the functoids available in BizTalk Server are available as map operations in Azure BizTalk Services and there are new Operations that doesn’t exist in BizTalk Server like: several List Operations, DateTimeReformat, GetContextProperty and so on. And despise XSLT and custom C# are supported the concept of “Scripting” map operation in Azure BizTalk Services Transforms are limited comparing to BizTalk Service for example:

  • You cannot use custom inline XSLT, you only can custom XSLT code directly to override WABS map, similar to the “Custom XSLT Path” map grid properties in BizTalk Server.
  • You can only use CSharp Scripting inside WABS maps.

Because of all this reasons you cannot directly use a BizTalk map in Azure BizTalk Services.

Basically you have two options that you can use to migrate BizTalk maps:

  • By using custom XSLT code gathered from your BizTalk Maps (…the easy way)
  • Or by using the BizTalk Map Migration Tool provided by Microsoft.

Using BizTalk Map Migration Tool

This is a command line tool, provided by Microsoft that takes a BTM File as input and that will try to produces the new mapper (.trfm) file as an output.

I’m saying “that will try” because there are many limitations present like:

  • Certain functoids may not get converted by this tool, for example some Scripting and Custom functoids from BTM way cannot be converted. In this case they are converted to arithmetic expression functoid in the TRFM map with the expression value as empty.
  • Links to functoids, which cannot be converted successfully, are denoted as empty inputs in the new TRFM functoids.
  • Certain features that are not supported, yet, in TRFM, will not be converted properly. For example, links from xs:extension node(s) are not converted to TRFM
  • Certain conditional mapping are not converted as expected as these features are not yet implemented in the Transforms
  • And so on.

At the end this tool will generate a conversion process report (Report.txt) listing out the failed conversions.

Also in terms of Organizing and Documenting Maps there are two drawbacks presents:

  • Labels and comments from the BTM functoids are not mapped onto the TRFM functoids
  • The multiple pages from BTM are translated to a single page in the TRFM map to ensure all operations within a scope are within a page.

The BizTalk Map Migration Tool is available on Codeplex at BTM Migration Tool, but before you use this tool you need to know that:

  • This tool expects that the machine on which it’ll run would have both products installed: BizTalk 2010 or above and Windows Azure Biztalk Services.
  • And the schemas should be present as expected by the BTM map (in the relative folders).

The BTM Migration Tool is a command line tool that uses the following parameters:

  • A BizTalk map (.btm)
  • Optional. Resulting BizTalk Service project Transform (.trfm)

BTM-Migration-Tool-cmd

Let’s look at one very easy example and analyze the end result. In this scenario we have two different records, Client and Employee, and we want to map all the man’s clients and employees to Person (Sex == M). This is look of the BizTalk Server maps:

BizTalk-Server-map-sample-to-WABS

When we apply the BTM Migration Tool it will generate this nice WABS map for us:

BizTalk-Services-map-sample-from-BTS

And the good news in this case, if we try the map it will successfully work and it will produce the tight output! (Yes I looked for a good sample). However is this the perfect output?

So let’s try change this to get only the female sex present in the input message. It seems an extremely easy change to make… we just need to go to the Logical Operation present in each MapEach Loop Operation and change the condition

BizTalk-Services-Logical-Operation-WABS-Condition

However surprisingly the output instance after this change is empty

WABS-map-empty-result

So why this happens? In this case, after looking to all the shapes, I found out that the MapEach Loop operation also as the same condition that is present in the Logical Operation that we need to change, as you can see in the picture bellow:

BizTalk-Services-MapEach-Loop-Operation-WABS-Condition

Can this map be improved?

The aim here is not to explain the functionalities of the new map and how the new operations works, however if you look at the map generated by the compiler, at least in this case, you can easily identify that we can simplify the map to be more easy to read and to maintain, by:

  • Maintaining the condition in the MapEach Loop operation
  • Suppress all the Logical and Conditional Assignment Operations that are inside the MapEach Loop operation
  • And use direct links to map the Name and Sex

BizTalk-Services-map-improved-from-BTS

BizTalk-Services-MapEach-Loop-Operation-WABS-Condition-2

So as a final note, even if the migrator tool did a decent job, you should always try to identify some bottlenecks, problems or overlaps and simplify it

Using Custom XSLT Code

Migrating maps from BizTalk Server solutions to Azure BizTalk Services could be one of the more complex artifacts to migrate (depending on map complexity)… or not!

The good news is that, you don’t necessary have to remake or migrate our BizTalk map to WABS map. As I mention earlier WABS maps support custom XSLT code, you can use a custom external XSLT file, similar to the “Custom XSLT Path” map grid properties in BizTalk Server to override WABS map and use custom XSLT to make the entire map transformation.

I try to avoid using Custom XSLT files for solving a mapping problem, for many reasons, however there are always exception. I do recommend using custom XSLT files:

  • If you are dealing with huge message and High Performance is one of the primary requirements (and even then this maybe can be avoid)
  • Or if you already have a custom XSLT file provided by another team, or from another system that you can re-use in your map. In this case don’t try to reinvent the wheel!

And because we are migrating a BizTalk Server map we already have the XSLT, so for me there is no point spending a few and in some case many hours trying to remake the same transformation in a different editor.

To extend your WABS maps to use custom XSLT code you must:

  • Go to the grid properties (or also call Transform design surface properties) in the Transform Designer, where you can see that there is a XSLT property in which we can extend the map to use custom XSLT code

BizTalk-Services-WABS-map-Transform-design-surface

Note: This time Microsoft did a nice work and if you notice in the grid page there is message that will help you.

Transform-design-surface-message

  • In Properties, click XSLT, and click the ellipses (…). A Configure XSLT dialog window is opened.

BizTalk-Services-configure-XSLT

  • To import an existing XSLT file, click Import XSLT. Browse to the .xslt or .xsl file and click Open. The imported XSLT populates the Configure XSLT dialog window. OR, enter the XSLT syntax in the Specify the XSLT below … window.

BizTalk-Services-configure-XSLT-import

  • Optional. To import an existing XML extension file, click Import EXTXML. Browse to the XML file and click Open. The imported XML populates the Specify the extension objects … dialog window. OR, enter the XML syntax in the Specify the extension objects … window.
  • Optional. Click Use XslCompiledTransform for better performance to transform XML data by compiling XSLT style sheets and executing XSLT Transforms. When the style sheet is compiled, it can be cached and reused. Otherwise, the XslTransform class is used; which is best when executing a Transform on one occurrence.
  • Click OK.

When XSLT is used, the Transform design surface is grayed out. XSLT can be specified for every Transform file (.trfm).

BizTalk-Services-WABS-map-Transform-design-surface-XSLT

How do I generate XSL output from the BizTalk Mapper Designer?

To generate a XSLT file that represent you BizTalk Map you just need to:

  • Open the BizTalk map in Visual studio
  • Right click on the map and select “Validate Map” option, this operation will generate the XSLT and you can see the path of generated XSLT in output window.

You can use this XSLT, without change it in most of the cases, to add in your WABS Maps.

Code Samples

All of this sample can be found and downloaded in Microsoft Code Gallery:


BizTalk Mapping Patterns and Best Practices eBook [Free] coming soon….

$
0
0

I publicly announce for the first time that I was working in an eBook about BizTalk Mapping Patterns and Best Practices, and that I was published for free, this March during BizTalk Summit 2014 London…

BizTalk-Mapping-Patterns-and-Best-Practices-eBook-soon

… At the time I said it would be published in mid-April or May but since then I’ve been slightly quiet, a few tweets and that’s it!

So how’s the book and when will it be published? This is the question that some people have asked me and that you probably are asking too, at least if you attended the event in London

Well I have some good news Smile… the book will be a reality and will definitely be for free. I’ve already made ​​the commitment to the BizTalk community and I’ll fulfill it. And the first chapters had already been sent to my awesome team of reviewers… I’m waiting for their feedback Smile!

However there are still many things to finish, the structure of the book may still suffer some changes (depending on the reviewers feedback), although it is unlikely that I make some radical changes in its current structure. And I still need to finish the main chapter “BizTalk Mapper Patterns” and one additional final chapter.

You also need to remember that this eBook will be for free and that all those involved are spending their free time to make this happen! Personally, the last few months have been difficult for me to find some additional free time to engage and finish this project… and I also cannot ask and demand nothing more from my reviewers, we all are busy people. I prefer to take a little more time and provide some quality work (I hope).

State of art and key point to take:

  • The book will be a reality and will definitely be for free
  • 12 patterns addressed in the eBook, currently 233 pages…
  • Estimated that 85% of the work is done
  • All demos are finished, finally! (is hard to invent some good and practical scenarios)
  • I promise that I will push my reviewer to send me the feedback (but they are not the blocking point)
  • I’m currently have some free time to finish the eBook!!! SmileSmile

I hope that soon I will be sending all the chapter to the reviewers and then depending on the feedback: improve it, fix it and finally publish it… so I ask you to have a little more patience.



BizTalk Mapper: Working With Nillable Values (xsi:nil="true")

$
0
0

Basically there are two properties inside the schema element decides whether an element can be absent from the document: Min Occurs and Nillable.

If Min Occurs property is set 0 then that element can be absent from the XML message but if it is set to 1 it has to be present though its value can be empty. This is useful to reduce the size of the document if only not all the elements are mandatory to end systems.

In other hand, if the Nillable property of the element is set to true, this will indicate that the value of an element in the document may be null. This NULL values will be expressed with xsi:nil = true attribute in the element, ex:

<IntExist xsi:nil="true" />

An element with the attribute xsi:nil = true explicitly means that the value is unavailable or unknown at that moment and sometimes the end system explicitly requires to be notified that the value of the element is NULL so that they can take appropriate action.

In this sample scenario we will have a 2 mandatory elements that can be nillable that we need to map to the destination schema. In this scenario all the destination elements are also mandatory and we need to fill them with a valid value or specify the nillable property as true: so if the element exist we need to map the correct source value otherwise we need to set the destination element as nillable.

The first element “DateExist” is a mandatory element that can be null. If null we need to set a null value in the destination element also as null, otherwise we need to map the source value. To accomplish that we need to:

  • Drag one IsNil Functoid from the Toolbox window onto the Grid.
    • Drag a link from the “NillValue” field element in the source schema to the IsNill Functoid
  • Drag one Nil Value Functoid from the Toolbox window onto the Grid.
  • Drag one Logical NOT Functoid from the Toolbox window onto the Grid.
  • Drag one Value Mapping Functoid from the Toolbox window onto the Grid.
  • To create a rule for mapping the value if the element is null
    • Drag a link from the IsNill Functoid to the Nil Value Functoid
    • Drag a link from the Nil Value Functoid to the “NillValueOutput” field element in the destination schema
  • Otherwise, To create a rule for mapping the value if the element different of null
    • Drag a link from the IsNill Functoid to the Logical NOT Functoid
    • Drag a link from the Logical NOT Functoid to the Value Mapping Functoid Functoid
    • Drag a link from the “NillValue” field element in the source schema to the Value Mapping Functoid
    • Drag a link from the Value Mapping Functoid to the “NillValueOutput” field element in the destination schema

Do the exact same logic for the second element present in the source schema.

BizTalk-Mapper-Working-With-Nillable-Values

Sometimes the maps are misunderstood and notorious for producing a lot of unnecessary code that may cause a in some cases lack of performance. So the question that we can and should ask is whether this is the best solution or not to address this type of operations. To respond this question we should also inspect the generated code produce by the BizTalk Mapper:

<xsl:variable name="var:v1" select="string(NillValue/@xsi:nil) = 'true'" />
    <xsl:variable name="var:v2" select="userCSharp:LogicalNot(string($var:v1))" />
    <xsl:variable name="var:v4" select="string(AnotherNilValue/@xsi:nil) = 'true'" />
    <xsl:variable name="var:v5" select="userCSharp:LogicalNot(string($var:v4))" />
    <ns0:OutputSchema>
      <xsl:if test="string($var:v1)='true'">
        <NillValueOutput>
          <xsl:attribute name="xsi:nil">
            <xsl:value-of select="'true'" />
          </xsl:attribute>
        </NillValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v2)='true'">
        <xsl:variable name="var:v3" select="NillValue/text()" />
        <NillValueOutput>
          <xsl:value-of select="$var:v3" />
        </NillValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v4)='true'">
        <AnotherNilValueOutput>
          <xsl:attribute name="xsi:nil">
            <xsl:value-of select="'true'" />
          </xsl:attribute>
        </AnotherNilValueOutput>
      </xsl:if>
      <xsl:if test="string($var:v5)='true'">
        <xsl:variable name="var:v6" select="AnotherNilValue/text()" />
        <AnotherNilValueOutput>
          <xsl:value-of select="$var:v6" />
        </AnotherNilValueOutput>
      </xsl:if>
    </ns0:OutputSchema>
  </xsl:template>

In fact is a pretty decent XSLT code but the reality is that it can be better, we don’t need to use any support variables and we can remove one if condition by replacing the xsl:if condition for one xsl:choose condition.

This is a very simple approach, easy to implement and readable that you should use even in small or large messages (transformations) but only if you have to deal with a small number of nillable element.

However applying this approach in transformation that will need to deal with a large number of nillable elements, can lead to two problems:

  • A lot of unnecessary XSLT code that can in fact and of course always depending in the size of the message can lead to some lack of performance
  • A lot of functoid shapes (4 Functoids) and links (7 links) for each element that can lead to lack of visual Readability

So can we improve this solution for transformations that needs to deal with a large number of nillable elements?

Well that’s the problem, there isn’t a simple solution for that. At the first look you may think that’s easy, just copy the XSLT code inside to a Scripting Functoid and optimize the XSLT code.

However by doing that you will receive an error:

error btm1050: XSL transform error: Unable to write output instance to the following <file:///C:\…\MapNillValuesWithCustomXSLT_output.xml>. Prefix ‘xsi’ is not defined.

The problem is that the nil attribute is defined in the XML Schema instance namespace, http://www.w3.org/2001/XMLSchema-instance (commonly associated with the prefix xsi) and this namespace is not declared by default in the XSL code generated by the BizTalk Mapper.

This namespace is automatically declare only if you use the Nil Functoids in the map.

So the normal solution here is… to bypass the BizTalk Mapper and generate an external XSLT code and add it to the map by specifying the Custom XSLT Path by:

  • Open the map
  • Click the grid zone and on the properties window there will be a “Custom XSLT Path” property.  Click the ellipses and navigate to the file containing the XSLT.

BizTalk-Mapper-Working-With-Nillable-Values-External-XSLT

You then can use a similar code to check and map the elements:

<xsl:choose>
  <xsl:when test="NillValue/@xsi:nil">
    <NillValueOutput>
      <xsl:attribute name="xsi:nil">
        <xsl:value-of select="'true'" />
      </xsl:attribute>
    </NillValueOutput>
  </xsl:when>
  <xsl:otherwise>
    <NillValueOutput>
      <xsl:value-of select="NillValue/text()" />
    </NillValueOutput>
  </xsl:otherwise>
</xsl:choose>

However applying this approach we have a major problem for me:

  • We lose all the BizTalk Mapper functionalities.
Workaround

Well, at least that I know, unfortunately there is no simple way to declared the xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance namespace to the stylesheet of the map.

However we can apply one small workaround, is not perfect but in most of the case it will solve my problems:

  • For only one of the nillable elements we need to use the Nil Functoids explained in the beginning of this post.
    • This will declare automatically the name xsi namespace for us.
  • In the rest of the elements we now can use Scripting Functoids with the optimized XSLT code described above

BizTalk-Mapper-Working-With-Nillable-Values-XSLT

I’m still working to find a better way but until then

You can download the source code from:

BizTalk Mapper: Working With Nillable Values (xsi:nil=”true”) (92.7 KB)
Microsoft | Code Gallery


Understanding and demystifying the Scope of the Table Looping Functoid

$
0
0

Let’s have a look to a little of inside information that will be in my upcoming Book about maps: demystify and clarify some features about the Table Looping Functoid.

First of all this functoid requires at least 3 inputs and a maximum of 100, in which the official documentation states that:

  • Parameter 1: A link from a repeating node in the source schema. The number of instances of this structure that occur in a particular input instance message defines the number of times that the associated table looping grid is processed.
  • Parameter 2: A constant input parameter that defines the number of columns in the associated table looping grid.
  • Parameters 3 – 100: A link from a node in the source schema or from another functoid, such as a Value Extractor functoid, or a constant input parameter. The relative order of parameters 3 – 100 is unimportant.

I think, at least for those who know the minimum about functoid, the last parameter(s), from 3 to 100, does not present any doubts. They are the values that we want to map from the source to the destination. And the order is not important because we need to define the Table Looping Grid property were we will define the order of appearance of these values.

The Table Looping Grid is basically a configurable table containing an arbitrary number of rows, configurable while editing the table, and a number of columns specified by the second input parameter of the Table Looping functoid. Each table cell is configured by using a drop-down list where the entries in the drop-down list are the values from to third through the last input parameters of the Table Looping functoid. These input parameters consist of a combination of the links into the functoid and any constant input parameters that have been defined. If link input parameters have been given a Label property value, that value is shown in the drop-down lists; otherwise, the value of the Link Source property is shown (generally, the former is friendlier than the latter). Constant input parameters are shown according to their constant value.

Important note: However the number of rows present in the Table Looping Grid are not defined in any input parameter from the Table Looping Functoid and in under no circumstances they are dynamic. The number of rows are statically defined during developing in order to apply a specific transformation rule and resolve a particular transformation problem.

Although people use this functoid correctly, especially if we are using it with a repeating record in the source schema, we will see in more detail further on in this chapter, is went we are trying to use this Functoid to transform a flat structure to a recursive structure that we realize that many developers don’t properly understand the first input parameter of this Functoid and to be honest, neither the explanation present in the official documentation will explain it correctly.

Although the Table Looping Functoid states that the first input must be a scoping element linked from a repeating group, that is not really true, in fact for me this description is complete false!

  • A link from a repeating node in the source schema – most common used but not entire true, you can also make use of a constant value, a simple node or an element.
  • The number of instances of this structure that occur in a particular input instance message defines the number of times that the associated table looping grid is processed. – I understand and in a way yes, but sometimes developers understand that this as the number of rows that they have to define in the table Looping Grid, with is not true, or that this input requires a number and in fact this is also not true.

So to be more clear, for me the best description of this parameter is:

  • Parameter 1: the first input parameter defines the action scope of the Table Looping Functoid and it can be defined by a link from a source tree node, repeating record, simple records or even elements, or by a constant value. This means:
    • If the scope is defined by a repeating record or repeating element, the Table Looping functoid will be created/execute in each occurrence of the record or element (in each iteration over the node). We can define this as multiple scope action.
    • If the scope is defined by a simple element or record (only occurs one time), than the Table Looping will be executed only one time. We can define this as simple scope action.
    • If the scope is defined by a simple constant value, regardless if it is an integer, alpha-numeric or string, than the Table Looping will be executed only one time. Again this is a simple scope action.

Table-Looping-Functoid-Scope-defined-by-a-simple-constant-value

Both this options are valid! More details will be found in my upcoming BizTalk Mapping Patterns and Best Practices free eBook.


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

$
0
0

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 for this problem we need to notice that two element 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 bellow:

Name-value-to-hierarchical-using-only-functoids

  • We need to repeat the above steps for all the element except the “IPRoute” element, until we get the following map:

Name-value-to-hierarchical-using-only-functoids-2

  • 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-3

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 in 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 windows
  • Right-click and select “View Source” option

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

<xsl:for-each select="Properties/Property">
  <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Name/text()) , &quot;Type&quot;)" />
  <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 , &quot;Protocol&quot;)" />
  <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 in 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 complete 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 work 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 don’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 validate 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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']) > 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

The sample code is available for download in Code Gallery:.

BizTalk Mapper Patterns: How to Map Name Value Pair to a Hierarchical Schema (59.2 KB)
Microsoft Code Gallery


BizTalk Mapper Patterns: How to map values from a repeating node into a single node using conditions

$
0
0

Well, I decided to take a few minutes of my vacation to play a little with … BizTalk, renew some knowledge, answer a few emails and maybe try to answer some questions on the forums.

This exercise (or pattern) is actually from a question that I found on the forums: Reg BizTalk Mapping, which I found interesting.

So what’s the best way to map some values from a repeating node into a single node base in some conditions?

Note: you can find all mapping logic of this exercise in the forum, however briefly we have a repeating node “TimeSeries” and based on Path attribute value of “TimeSeries” node we will map on some elements of the output message:

  • If “Path” attribute == “1” then assign the value of “TimedValue” to “Quantity”
  • If “Path” attribute == “2” then assign the value of “TimedValue” to “NRJQuantity”
  • If “Path” attribute == “3” then assign the value of “TimedValue” to “AvgCal”
  • If “Path” attribute == “4” then assign the value of “TimedValue” to “AvgDens”
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 attribute “Path” in the source schema to this functoid, this will be the first input parameter in the functoid
    • And in the second parameter we need to put the number that we want to find, in this case: “1”.
  • Drag a Value Mapping functoid to the grid
    • Drag a link from the Equal functoid to this Value Mapping functoid
    • Drag a link from the “TimedValue” element in the source element to the Value Mapping functoid
  • Drag a link from the Equal functoid for the element in question in the destination schema, in this case “Quantity” element
  • And finally we need to drag a link from the Value Mapping functoid to the respective element in the destination schema, in this case again “Quantity” element as you can see in the picture bellow.

map-values-from-repeating-node-into-single-node-using-conditions-with-functoids

  • We need to repeat the above steps for all the element until we get the following map:

map-values-from-repeating-node-into-single-node-using-conditions-with-functoids-all

This solution is correct and in fact is what’s normally we found in this type of mapping problems however this is not the best option in terms of performance. If we analyze the XSLT regenerated by the BizTalk mapping engine by:

  • Right-click in 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 windows
  • Right-click and select “View Source” option

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

<ns0:Req>
  <xsl:for-each select="TimeSeries">
    <xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(@Path) , &quot;1&quot;)" />
    <xsl:if test="$var:v1">
      <xsl:variable name="var:v2" select="string(@Path)" />
      <xsl:variable name="var:v3" select="userCSharp:LogicalEq($var:v2 , &quot;1&quot;)" />
      <xsl:if test="string($var:v3)='true'">
        <xsl:variable name="var:v4" select="TimedValues/TimedValue/text()" />
        <ns0:Quantity>
          <xsl:value-of select="$var:v4" />
        </ns0:Quantity>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>

  <xsl:for-each select="TimeSeries">
    <xsl:variable name="var:v5" select="string(@Path)" />
    <xsl:variable name="var:v6" select="userCSharp:LogicalEq($var:v5 , &quot;2&quot;)" />
    <xsl:if test="$var:v6">
      <xsl:if test="string($var:v6)='true'">
        <xsl:variable name="var:v7" select="TimedValues/TimedValue/text()" />
        <ns0:NRJQuantity>
          <xsl:value-of select="$var:v7" />
        </ns0:NRJQuantity>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>
....

This means that if we have 50 occurrences of “TimeSeries” node, we will have 50 iterations for each element that we want to map in the destination schema… so this approach may be easy to implement and somewhat acceptable in small messages is extremely disadvantageous for large message.

Limitations of this approach:

  • Lack of performance
Second Solution: Using Inline XSLT

In this second approach what we will do is take the XSLT code generated by the compiler and optimize it by removing all unnecessary cycles and put this code in a Scripting functoid.

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="TimeSeries">
  <xsl:if test="string(@Path) = '1' ">
    <Quantity>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:if>
  <xsl:if test="string(@Path) = '2' ">
    <NRJQuantity>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </NRJQuantity>
  </xsl:if>
  <xsl:if test="string(@Path) = '3' ">
    <AvgCal>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </AvgCal>
  </xsl:if>
  <xsl:if test="string(@Path) = '4' ">
    <AvgDens>
      <xsl:value-of select="TimedValues/TimedValue/text()" />
    </AvgDens>
  </xsl:if>
</xsl:for-each>
  • Finally drag a link from the Scripting functoid to one element in the destination schema, for example “NRJQuantity”

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting

Limitations of this approach:

  • May have some lack of performance if we work with large message because some unnecessary iterations in the cycle, however it is far more efficient than the first solution.
  • Some warnings saying that some required field has no incoming link.
  • Because we use scripting functoids we cannot read the entire map visually. We need to open the functoids and read, mainly, the XSLT code.
Third Solution: Using Inline XSLT along with XPath queries

After analyzing all the advantages and disadvantages, I decided that I could optimize even more the XSLT script in order to have a better performance but to do this I would have to use a different approach than the one that was used by the BizTalk mapper engine, and for me this is the best approach to accomplish this type of mapping problem, because basically solves all limitations of previous solutions: it’s easy to create (only need basic knowledge of XSLT and XPath) and don’t have performance problems.

To accomplish this, we need to:

  • Replace the code of the Scripting functoid, existing in the previous solution, by:
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='1']) > 0">
    <Quantity>
      <xsl:value-of select="//TimeSeries[@Path='1']/TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='2']) > 0">
    <NRJQuantity>
      <xsl:value-of select="//TimeSeries[@Path='2']/TimedValues/TimedValue/text()" />
    </NRJQuantity>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='3']) > 0">
    <AvgCal>
      <xsl:value-of select="//TimeSeries[@Path='3']/TimedValues/TimedValue/text()" />
    </AvgCal>
  </xsl:when>
</xsl:choose>
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='4']) > 0">
    <AvgDens>
      <xsl:value-of select="//TimeSeries[@Path='4']/TimedValues/TimedValue/text()" />
    </AvgDens>
  </xsl:when>
</xsl:choose>

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting-2

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
  • Some warnings saying that some required field has no incoming link.
Fourth Solution: Using Inline XSLT along with XPath queries (avoiding warnings)

So to avoid warnings saying that some required field has no incoming link we must split the XSLT code that we use in the last solution (Third Solution) in different blocks for each element in the destination schema

To accomplish this, we need to:

  • Drag four Scripting functoid to the map grid and drag a link from each Scripting functoid to each element in the destination schema
  • For each Scripting functoid:
    • In the scripting type select “Inline XSLT” option
    • In the Inline script put the code that corresponding to the element in the destination element, for example in the first:
<xsl:choose>
  <xsl:when test="count(//TimeSeries[@Path='1']) > 0">
    <Quantity>
      <xsl:value-of select="//TimeSeries[@Path='1']/TimedValues/TimedValue/text()" />
    </Quantity>
  </xsl:when>
</xsl:choose>

map-values-from-repeating-node-into-single-node-using-conditions-with-scripting-3

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

The sample code is available for download in Code Gallery:.

How to map values from a repeating node into a single node using conditions (142.6 KB)
Microsoft Code Gallery


BizTalk Mapper Patterns: Automatically Link The Record Elements By Structure, Name or Using Mass Copy

$
0
0

Exchanging or routing messages between existing applications, systems or external partners is one of the many common scenarios in Enterprise integration solutions. And sometimes they are build base on the same data model, however because they are built in different systems and teams they can have slight small differences like: the order of the elements, the name of the elements, the structure or even the namespace. As a result of that we need to be able to transformed the original message to the expect message by that systems.

BizTalk Mapper provides you with just-in-time assistance, through a shortcut menu, when you create links between two record elements of source and destination schemas:

  • You can create multiple links simultaneously if:
    • Relevant schema structures must be the same (node names are different but order is the same or similar;
    • Or Record, elements or field names must match;
  • You can manually create single links by dragging and dropping the source element to the destination element, if the destination schema don’t have a similar structures or the same names;
  • Or you can use Mass Copy Functoid to copy the element in the input instance message to a destination schema that use the <any> Element (some kind of generic schema). This functoid also copies any and all of its substructure, and re-creates it in the output instance message at the linked node in the destination schema.

The BizTalk Mapper provides you with just-in-time assistance, through a shortcut menu, when you create links between two record elements of source and destination schemas.

So for accomplish that, open your map and:

  • Drag-and-drop the root name of the source schema, in this case “PurchaseOrder” to the root name of the destination schema, which will be in this case “PO”, and release the direct button of the mouse.

just-in-time-assistance-BizTalk-Mapper

  • A windows assistance will pop up were you can select create record-to-record links automatically in the following ways:
    • Direct Link: Using this technique, the BizTalk Mapper links the record from source schema to the selected record in the destination schema. This will not copy any kind of hierarchy structure from the source to the destination, it will only link the record or node.
    • Link by Structure: Using this technique, the BizTalk Mapper attempts to match the Record and Field nodes within the Record nodes being linked according to the structures of those Record nodes, regardless of names of the corresponding nodes within those structures.
    • Link By Name: Using this technique, the BizTalk Mapper attempts to match the Record and Field nodes within the Record nodes being linked according to the names of the corresponding nodes, regardless of their structure, within the Record nodes being linked.
    • Mass Copy: The Mass Copy functoid enables your maps to use schemas that include any and anyAttribute elements. For information about the functoids available in BizTalk Mapper.

Our scenario will have a Purchase Order that will need to be mapped to 3 different systems with 3 different target schemas.

  • The System A have a similar schema of our system, with the same structure and the same names, but they are presented in a different order;
    • Because the destination schema have the same names of the source schema, the best approach in this scenario is to use the Link By Name option
  • The System B have also a similar schema of our system, we as the same order and the same schema structure, however the elements have different names;
    • Because the destination schema have the structure of the source schema, the best approach in this scenario is to use the Link By Structure option
  • The System C have a canonical schema (or generic schema) that will accept any structure.
    • Because the destination schema will accept any kind of structure, the best approach in this scenario is to use the Mass Copy option
Scenario A: How to link the record elements by name

To accomplished that we need:

  • Drag the mouse from the root name “PurchaseOrder” in the source schema, and then drop it to root name “PO” in the destination schema.
  • On the shortcut menu, click “Link by Name” option. The BizTalk Mapper will attempt to match all the Record, Elements and Field nodes under the “PurchaseOrder” node according to their names, regardless of their structure, within the node “PO”.

And the BizTalk Mapper will automatically map all the elements from the source schema to the target schema that have the same name, as the picture bellow will show:

Link-by-Name

Of course this technique as some limitations, the elements need to have the exact same name, and will only work in some scenarios.

Scenario B: How to link the record elements by structure

To accomplished that we need:

  • Drag the mouse from the root name “PurchaseOrder” in the source schema, and then drop it to root name “ExternalPO” in the destination schema.
  • On the shortcut menu, click “Link by Structure” option. The BizTalk Mapper will attempt to match all the Record, Elements and Field nodes under the “PurchaseOrder” node according to the structure of those elements, , regardless of names of the corresponding nodes within those structures, within the node “ExternalPO”.

And the BizTalk Mapper will automatically map all the elements from the source schema to the target schema that have the same structure, as the picture bellow will show:

Link-by-Structure

Again this technique have the same limitations of the previous one.

Scenario C: How to link using a mass copy functoid

To accomplished that we need:

  • Drag-and-drop the Mass Copy functoid from the Toolbox to the map grid page and then:
    • Link the source root name “PurchaseOrder” as input parameter for the Mass Copy functoid;
    • Link the Mass Copy functoid to the destination root name “CanonicalPurchaseOrder” to set the output parameter from the Mass Copy functoid;

Mass-Copy

The only problem in using the Mass Copy functoid is that it will move all the elements and values from the source schema to the destination schema but it also will include the targetNamespace of source schema in the destination schema and this behavior can be a problem in some scenarios.

Example of the map output:

<ns0:CanonicalPurchaseOrder xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.CanonicalPurchaseOrder">
 <From xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">From_0</From>
 <To xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">To_0</To>
 <LineItems xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.PurchaseOrder">
 <Item>
 <Product>Product_0</Product>
 <Description>Description_0</Description>
 <Price>10</Price>
 <Quantity>10</Quantity>
 </Item>
 </LineItems>
</ns0:CanonicalPurchaseOrder>

So how can we avoid this behavior?

However sometimes the destination schema expect a different message, without the namespaces in the element:

<CanonicalPurchaseOrder xmlns:ns0="http://BizTalkMapperSemanticTranslatorPattern.CanonicalPurchaseOrder">
  <From>From_0</From>
  <To>To_0</To>
  <LineItems>
     <Items>
        <Product>Product_0</Product>
        <Description>Description_0</Description>
        <Price>10</Price>
        <Quantity>10</Quantity>
     </Items>
  </LineItems>
</CanonicalPurchaseOrder>

I already saw this scenario many times. So how can we accomplish this?

There is no easy way to accomplished this without the need to use custom XSLT and you will need to customize the syntax to your scenario.

In this sample if we need to map everything without namespaces we need to “replace” the Mass Copy functoid for a Scripting functoid:

  • Drag-and-drop the Scripting functoid from the Toolbox to the map grid page and then:
    • And Link the Scripting functoid to the destination root name “CanonicalPurchaseOrder” to set the output parameter from the Scripting functoid;
    • This functoid will not have any input parameters;
  • Double-click in the Scripting functoid to show the Properties window, then:
    • Select the “Script Functoid Configure” tab, and then choose “Inline XSLT” as your selected script type
    • In the “Inline script” text box enter the following XSLT code without the comments (they are only for explaining the operation of the script):
<CanonicalPurchaseOrder>
      <xsl:for-each select="/s0:PurchaseOrder/*">
        <xsl:if test="local-name()!='LineItems'">
          <xsl:element name="{local-name(.)}">
            <xsl:value-of select="." />
          </xsl:element>
        </xsl:if>
      </xsl:for-each>

      <xsl:choose>
        <xsl:when test="count(/s0:PurchaseOrder/LineItems) &gt; 0">
          <LineItems>
            <xsl:for-each select="/s0:PurchaseOrder/LineItems/*">
              <Items>
                <xsl:for-each select="./*">
                  <xsl:element name="{local-name(.)}">
                    <xsl:value-of select="." />
                  </xsl:element>
                </xsl:for-each>
              </Items>
            </xsl:for-each>
          </LineItems>
        </xsl:when>
      </xsl:choose>
</CanonicalPurchaseOrder>

Basically what we are doing is:

  • The Scripting functoid will not add the root name, so we need to manually add it
  • The we are Getting all the element inside “PurchaseOrder” but because we need to travel all the elements inside the LineItems we need to treat this different, so we will get all the element except the ” LineItems”
  • The next step is check if there is “LineItems”
  • If exist we will travel all the element inside the LineItems (for each)
  • Because the “LineItems” only contain one record we will add this manually but we need an additional cycle this time to travel all the elements inside the record Item

And with that we create our own custom Mass copy Functoid

The sample code is available for download in Code Gallery:.

Automatically Link The Record Elements By Structure, Name or Using Mass Copy (148.5 KB)
Microsoft Code Gallery


Viewing all 36 articles
Browse latest View live