Part 1 of this article series, Using the XML Mapping Editor to develop maps, showed you how to quickly create robust and well-organized XML maps using IBM® WebSphere® WebSphere Integration Developer V6.1.2. It also described tools you can use to test and debug maps to make problem determination easier. This part, Part 2, shows you how to perform more complex XML mapping.
Some fields in the source or target may be repeatable, in which case there is an array of specific elements. As a general rule, when you are working with arrays, you should always start by mapping the input array to the output array which creates a For-each transform. For Example:
It is not recommended that you directly map children of an array element without using a For-each on the parent array. For example, this is not a recommended way of accomplishing a mapping similar to the one above:
By default, when you wire an input array to a For-each transform, it causes iteration over all elements in the input array, whereby each array element will be mapped according to the nested mappings defined within the For-each mapping.
Similar to a Local map, For-each is a container mapping that does not produce any results unless there are nested mappings that define how to operate over each iteration of the input array. Therefore, you must specify the specific details about what fields in the source get mapped to what fields in the target to complete the For-each implementation. For example, here are nested mappings within a For-each mapping:
When working with arrays, here are some other scenarios you may encounter.
Scenario 1. Mapping from a source array to a singleton target field
As with the general case for dealing with an array, you should still start by creating a mapping between the source array element and the target field. In this case that top-level mapping will be a Local map or Move, since a For-each only applies when the source and target are arrays. In this specific case, you need to decide which input array element you want to work with. The applicable array index can be set on the cardinality tab of the properties page for the Local map or Move transform. When specifying an index on the cardinality tab, indices start from 1 rather than 0.
To illustrate this case, consider the following example, with the following source and target:
In this case, the source contains an array of destination objects that have a city and country name, but the target expects only one city name and one country name. In this case, you want to take the first entry of the destination array and map its city and country name to the target singleton. The first step is to create a Local map between the array and the target item that contains the fields you want to populate.
Once that Local map is created, set the cardinality to indicate that the first array entry will be the input to the Local map transform. Within the Local map, the fields of the destination element are matched to the appropriate fields of the GetWeather element:
Scenario 2. Mapping from a singleton source field to an array in the target
In this case, assuming the target array contains only a single element, you should once again create a Local map between the source field and the target array. Within the Local map, fill in the details about what fields in the source map to which fields in the target.
You can populate more than one element in the target array by creating multiple Local maps to the array and specifying different indices on the cardinality tab of the properties pages. You can also use a Merge transform to use information from two or more different singletons in the source to populate an array element in the target. When specifying the output array index on the cardinality tab, indices start from 1 rather than 0.
Scenario 3. Mapping from multiple arrays in the source to a single array in the target
In some cases you may have two or more arrays in the source that you want to use to create a single array in the target. In this case, there are two possible results you might want. One result may be to merge entries in the input arrays to form a single entry for the target array. Another desired result might be to append the entries from the input arrays together to form a longer output array. Both the merge and append cases are explained in below.
Case 1 -- Merge
In this case, you want to merge two arrays from the source into a single array on the target. For example, consider the case where the source contains two separate arrays; an array of names and an array of phone numbers. Assuming that the phone number list has a one-to-one correspondence with the name list, and your output array contains elements that require a name and phone number, you could merge the information from the two separate arrays into a single array on the target side.
In this case, the Merge transform can be used to accomplish the desired result. Similar to a For-each, a Merge transform is a container mapping that does not produce any results unless there are nested mappings that define how to operate over each iteration of the input arrays. Therefore, you must specify the details about what fields in the source get merged to what fields in the target to complete the Merge implementation. Note how iterations are performed in a Merge: the process starts by taking the first index of all input arrays and merging those fields into the first index in the target array. Then the second index of all input arrays are taken to produce the second index of the target array, and so on. An easy way to think of a merge is that if you take two same-sized arrays and merge them together, you get a target array that is also the same size as the original arrays. For example:
In cases where the sizes of the input arrays of a Merge transform differ, you can specify which input you would like to iterate on the Cardinality property page. By default, iteration will be set to the first input of the Merge transform. However, specifying which input array to iterate over when the sizes of the input arrays differ will produce different results on the target array. Take the following two examples as an illustration of the differences when the input array sizes are known to be different. For example:
In the above examples, the target array sizes produced are directly dependent on which input array was selected to iterate over. In cases where the input array sizes are known to be the same during the execution of the map, it does not matter which input is selected to iterate over since the same result will be produced.
Case 2. Append
In this case, you want to take all the elements from one source array and all the elements from another source array and create an array on the target side that contains all the elements from both the source arrays. For example, suppose you have two lists of travel destinations on the source and you want to create a single list on the target that contains all the destinations.
In this case, the Append transform can be used to accomplish the desired results. An easy way to think of an Append is that if you take two arrays of any size, and append them together, you get a target array that is the sum of size of the input arrays. For Example:
Similar to a For-each, an Append transform is a container mapping that does not produce any results unless there are nested mappings that define how to operate over each iteration of the input arrays. Therefore, you must specify the details about how fields in the source get added to the fields in the target to complete the Append implementation. When implementing an Append transform, the first layer of nested mappings is automatically generated for you with For-each and Move mappings. It is within the nested For-each mappings that further mappings are needed to complete the implementation. Iterations are performed in an append by taking the first index of first input array to produce the first index in the target array. Then the second index of first input array is taken to produce the second index of the target array. This process continues until all elements of the first input have been operated over, after which point all elements of the second input array are processed (one-by-one), and so on. The order in which input arrays are appended is based on their order going into the Append transform. If desired, you can modify this in the Order Property page on an Append transform.
When using a source array as an input to the transform, you have the option of having the input array elements sorted prior to any mappings taking place. This can be done using the properties pages of the For-each transform. You can specify the field in the array that you want to use for sorting. You can also specify whether a lexical or numerical search will be used and whether to sort ascending or descending. For example, suppose we have an array of destinations where each destination has a city name and a country name. Suppose we want to sort the list alphabetically in ascending order based on the city name. We could use the properties page of the For-each transform to specify a sort as follows:
Sometimes while working with an array input, you may want to work with only specific elements in the array. If this is the case, you can either specify the applicable array indices on the Cardinality tab of the Properties view, or you can apply a condition to the transform.
Cardinality
The cardinality tab can be used to determine which array indices will be used as input to the transform. You can specify a single index, ranges of indices, or a comma separated list of indices. The following chart shows the accepted special characters.
Cardinality syntax
| Character | Meaning |
|---|---|
| : | Used to specify a range of values. |
| , | Delimiter to separate a list of indices and/or ranges |
| * | All indices / Until the end of the array |
Here are some examples of how the above special characters can be used to specify cardinality:
Cardinality syntax examples
| Value | Meaning |
|---|---|
| 1 | Only element 1 |
| 1:3 | Elements 1 through 3 |
| 2:* | Elements 2 and up |
| 1,3,5:* | Elements 1, 3, 5 and up |
Array indices start at 1 rather than 0.
Filtering using conditions
While working with an input array, you can use a condition on the transform to filter the desired elements from the source array. In this case, the condition will be used to determine which elements from the source array are selected. The condition should be written to return the node set that the For-each will operate on. In order to return the appropriate node set, the condition should be written as a predicate rather than a true/false condition. For example, suppose that we have an array of travel destinations where each destination has a city name and a country name. Suppose we want to eliminate any destination where the country name is equal to North Pole, in this case, we could add a condition to the For-each transform as follows:
All destinations that were not set to the North Pole would make it as input into the For-each mapping. Destinations set to the North Pole would be ignored and would not be part of the input into the For-each mapping.
The Group refinement provides support for converting an array to a nested array. For example, suppose you had an array of travel destinations where each destination in the list had a category to indicate what type of travel destination it was. Categories might include Beach, Adventure, etc. If you wanted to create a nested array from these destinations where the categories would form the top-level array and the destinations in each category would form the nested arrays, a Group transformation could be used. The following diagram shows the Group transformation being used to group the destinations by category into a nested array. Notice that in the Properties page for the Group transform, the category field is specified as the grouping criteria.
Similar to a Local map, Group is a container mapping which does not produce any results unless there are nested mappings that define how to operate over each iteration of the input array. Therefore, you must specify the details about what fields in the source get mapped to what fields in the target to complete the Group implementation.
There is currently no transform that will allow you to ungroup a nested array into a single array. However, you can use a custom XSLT transform to manipulate a nested array into a single array. For an example of how that could be accomplished, see the Custom XSLTsection of the article.
In some cases you may find that there are no ready-made refinements appropriate to perform the data manipulation required to correctly populate a target. In this case, a custom transform can be used. Custom transforms can be implemented using XPath, XSLT, or Java. The properties page of a custom transform allows you to select an implementation type and specify implementation details.
In some cases there might be an existing XPath function or operator that can manipulate the data as required. In this case, you can write a custom XPath expression to achieve the desired result.
While writing XPath expressions, the inputs to the custom transform are available as variables. You can refer to them using the following syntax: $<variablename>.
For example, if the transform has an input element named currentTemperature, then you could refer to that element using $currentTemperature.
For an example of using an XPath operator in an expression, consider a scenario where you want to use the sum of two inputs of type xsd:int to produce an xsd:int result. You could create a Custom transform
with the two int inputs wired to the transform and then use an expression such as the following to sum the two inputs $inputx + $inputy.
Another example would be if you wanted to use the string-length function, you could use a custom transform with the following expression: string-length($mystring).
Invoking content-assist (using CTRL+SPACE) in the XPath expression entry field will display a list of available variables as well as a list of XPath functions that can be easily inserted into the expression.
Custom transforms can be implemented using XSLT. When using XSLT, a named template in an external file is called to produce the target output. Inputs to the Custom transform can be sent to the custom XSLT as parameters. Common XSLT templates can be stored in a library and shared by many mappings. Custom XSLT can be useful for creating elements in the target that do not exist in the source. Usually, a Submap could also be used to accomplish this but there are cases where a Submap will not work. Some things to keep in mind when working with Custom XSLT are:
- The target element of the Custom mapping will be created automatically and the template that is called will only be able to populate the contents of the target element. If you need to use Custom XSLT to create the entire target element (for example, you want to set an attribute on the target element), you need to create the Custom mapping on the target elements parent element instead of on the target element itself.
- Within a custom XSLT template, if you need data from a node other than the input node to complete your template, you can wire the node as an additional input to the Custom transform. The other alternative is to use an absolute XPath expression.
As an example of using custom XSLT, consider the scenario where there is a nested array on the source. The outer array is a list of categories and each category contains a list of travel destinations that match the category.
Sample data
| Travel Category: Beach |
|---|
| Acapulco, Mexico |
| Veradero, Cuba |
| Miami, United States |
| Travel Category: Adventure |
| Whitehorse, Canada |
| Grand Canyon, United States |
Sample map with custom XSLT transform

The above custom transform will call the following XSLT template passing each category to the template:
<xsl:template name="CategoriesToAvailableDestinations">
<xsl:param name="category"/>
<xsl:for-each select="$category/travelDestination">
<availableDestination>
<xsl:copy-of select="destination"/>
<category><xsl:value-of select="../categoryName"/></category>
<popularity><xsl:value-of select="popularity"/></popularity>
</availableDestination>
</xsl:for-each>
</xsl:template>
|
The template pulls out the destinations from each category using an xsl:for-each:
<xsl:for-each select="$category/travelDestination"> |
The template also sets the category value in the output destination based on the parent category for the nested array.
<category><xsl:value-of select="../categoryName"/></category> |
Given the sample input data, the availableDestinations output array would be as follows:
<availableDestinations> <availableDestination> <destination> <cityName>Acapulco</cityName> <countryName>Mexico</countryName> </destination> <category>Beach</category> <popularity>8</popularity> </availableDestination> <availableDestination> <destination> <cityName>Veradero</cityName> <countryName>Cuba</countryName> </destination> <category>Beach</category> <popularity>7</popularity> </availableDestination> <availableDestination> <destination> <cityName>Miami</cityName> <countryName>United States</countryName> </destination> <category>Beach</category> <popularity>7</popularity> </availableDestination> <availableDestination> <destination> <cityName>Whitehorse</cityName> <countryName>Canada</countryName> </destination> <category>Adventure</category> <popularity>7</popularity> </availableDestination> <availableDestination> <destination> <cityName>Grand Canyon</cityName> <countryName>United States</countryName> </destination> <category>Adventure</category> <popularity>9</popularity> </availableDestination> </availableDestinations> |
To continue the example, assume that we did not actually want to take all the categories of destinations and flatten them into a single list, but rather we want to take only the destinations from a single category and flatten those into a list. Lets assume that the customer is going to let us know what category of destinations they are interested in. The following map shows the customers selected travel category and the list of categorized destinations being passed into the Custom XSLT transform.

The above map calls the following XSLT template to find the destinations from the applicable category only
<xsl:template name="CategoriesToAvailableDestinations">
<xsl:param name="category"/>
<xsl:param name="customerTravelCategory"/>
<xsl:for-each select="$category/travelDestination">
<xsl:if test="../categoryName=$customerTravelCategory">
<availableDestination>
<xsl:copy-of select="destination"/>
<category><xsl:value-of select="../categoryName"/></category>
<popularity><xsl:value-of select="popularity"/></popularity>
</availableDestination>
</xsl:if>
</xsl:for-each>
</xsl:template>
|
In the above example, notice the following:
- The travelCateogory element was added as a second input on the Custom transform so that its value could be available as a parameter in the custom template.
- The following if statement ensures that only the destinations from the appropriate category are chosen:
<xsl:if test="../categoryName=$customerTravelCategory">
Using built-in extensions in XSLT
The XSLT processor used by XSL Transformation primitives, when running in the WebSphere Process Server and WebSphere Enterprise Server Bus runtimes, is based on the Apache Xalan-Java XSLTC processor, which provides EXSLT extension functions that include string functions, math functions, set functions, and date and time functions. For a list of EXSLT functions, see the Apache XML Project.
There may be cases where the IBM XSLTC processor differs from the Apache Xalan-Java XSLTC processor, and therefore all of the functions mentioned above may not be available. The following example shows the ExstlString:split function being used in a custom XSLT template. In the example, the GetWeather service has returned a string of XML where somewhere in the string there is a temperature value that we want to extract. For example:
... ... <Temperature> 55 F (13 C) </Temperature> ... ... |
XSLT
...
xmlns:str="http://exslt.org/strings"
...
<xsl:template name="GetWeatherResultToCurrentTemperature">
<xsl:param name="GetWeatherResult"/>
<!-- Get all text after the <Temperature> tag in GetWeather output string -->
<xsl:variable
name="temp1"
select="str:split($GetWeatherResult, '<Temperature>')[2]"/>
<!-- Get only the text before </Temperature> in the already reduced string -->
<xsl:variable
name="temp2"
select="str:split($temp1, '</Temperature>')[1]"/>
<!-- Remove leading and trailing white-space before returning the result -->
<xsl:value-of select="normalize-space($temp2)"/>
</xsl:template>
|
Notice that:
- The str namespace is defined on the stylesheet as follows:
xmlns:str="http://exslt.org/strings" - The split function is called with 2 string arguments as follows:
select="str:split($GetWeatherResult, '<Temperature>')[2]"
Using Common XSL files Located in Libraries
It is recommended that common XSL templates be placed in custom XSL files located in library projects. By using libraries, multiple mediation modules can get access to the common XSL templates.
When importing a common XSL file into an XSL file located in a mediation module, an xsltcl URI should be used. For example, if the XSL file in your mediation module is
MediationModule1/xslt/MyMap-custom.xsl, and the XSL file that you want to import is /Library1/common/CommonXSLLibrary.xsl, then the import would be:
<xsl:import href="xsltcl://common/CommonXSLLibrary.xsl"/>.
The name of the library â Library1 â is not included in the URI.
The third method of implementing a Custom transform is using calls to static Java methods. You can populate the value of any simple type element using a Java call. You can wire inputs to the Custom Java transform and use those inputs as method arguments. Inputs can be simple or complex types. When using complex type inputs as method arguments, the corresponding method should be expecting a parameter of type org.w3c.dom.Node or org.w3c.dom.NodeList for arrays of complex types. When working with Custom Java transforms, the Properties page of the Custom transform allows you to select the desired class and method as well as map the inputs of the Custom transform to parameters of the Java method.
To illustrate the basic use of a Custom Java call, consider this example. During a mediation flow, a call to a web service that checks the weather at a particular destination is made. The weather service returns an XML result and the temperature of the destination is buried in that result. A static Java method will be used to parse the XML data and extract only the temperature as an int value
XML output as returned by Web service
<CurrentWeather> <Location>Toronto Pearson Int'L. Ont., Canada (CYYZ) 43-40N 079-38W 173M</Location> <Time>Oct 24, 2008 - 05:00 PM EDT / 2008.10.24 2100 UTC</Time> <Wind>from the ESE (110 degrees) at 9 MPH (8 KT):0</Wind> <Visibility>15 mile(s):0</Visibility> <SkyConditions>overcast</SkyConditions> <Temperature>50 F (10 C)</Temperature> <DewPoint>41 F (5 C)</DewPoint> <RelativeHumidity>71%</RelativeHumidity> <Pressure>30.14 in. Hg (1020 hPa)</Pressure> <Status>Success</Status> </CurrentWeather> |
Suppose that we are interested in extracting the Celsius version of the temperature, which for the above example would be 21. Given the above data, the following Java method will extract the Celsius temperature by finding the <Temperature> tags in the output.</Temperature>
public static int getCelsiusTemperature(String originalWeatherReport) {
int temperature = -99; // Error - Can't find temperature
if (originalWeatherReport != null)
{
int temperatureStartIndex = originalWeatherReport
.indexOf(TEMPERATURE_START_TAG);
int temperatureEndIndex = originalWeatherReport
.indexOf(TEMPERATURE_END_TAG);
if ((temperatureStartIndex >= 0)
&& (temperatureEndIndex > temperatureStartIndex))
{
temperatureStartIndex = temperatureStartIndex
+ TEMPERATURE_START_TAG.length();
String temperatureString = originalWeatherReport.substring(
temperatureStartIndex, temperatureEndIndex);
int startBracketIndex = temperatureString.indexOf('(');
int endBracketIndex = temperatureString.indexOf(')');
if (startBracketIndex >= 0 && endBracketIndex >= 0
&& (startBracketIndex < endBracketIndex))
{
String celsiusString = temperatureString.substring(
startBracketIndex + 1, endBracketIndex);
if (celsiusString.endsWith("C"))
{
celsiusString = celsiusString.substring(0,
celsiusString.length() - 1);
celsiusString = celsiusString.trim();
try {
double doubleValue = Double.parseDouble(celsiusString);
temperature = (int) Math.round(doubleValue);
} catch (NumberFormatException e) {
}
}
}
}
}
return temperature;
}
|
To use the above method in a Custom transform, we would need to do the following:
- Ensure that the library or module that contains the map file has a dependency on the project that contains the Java file containing the method.
- Create the Custom transform in the appropriate map file. For example:
- On the 'General' properties page for the Custom transform, select the Java radio button
- Use the Browse button beside the Class field to browse for the class that contains the method. As you type class names in the Select Type dialog, the list of available options will be updated to include classes that match the typed prefix.
- In the Method drop down box, select the desired method
- In the parameters section, choose the appropriate input field to map to each of the method parameters. You can use variables to refer to any input parameter by prefixing the input parameter name with $. For example, the following properties page references the GetWeatherResult value as a variable:
Debugging custom Java calls
When you have a map that uses a custom Java call, you can debug the Java that is being called when the map is running on the server. Setting breakpoints in the Java code will have no affect when using the Test Map view or when using the Integration Test Client to test a map file locally. However, if you are doing a component test using the Integration Test Client, and the server is running in Debug mode, breakpoints that you have set in called Java methods will cause the execution to stop so that you can step through the Java method.
Using custom Java to map a complex type target
When working with custom Java transforms, if the output of the transform is a complex type, the data in the output complex type will not get populated, even if your Java method returns a proper org.w3c.dom.NodeList. In order to populate a target that is a complexType using Java, you need to wrap the Java method call in a Custom XSLT transform. The following example shows how a complex type can be populated using a NodeList generated by a Java method.
For example, suppose you have a list of destinations on the source side, and want to use a Java method to add some additional destinations to the list before populating the list of destinations on the target side. To do so, start by creating a map that uses a custom XSLT transform to map the source destinations to the target destinations:

Once you have the custom XSLT transform, create a Java class to do this:
- Generate the list of additional destinations
- Merge the existing destinations with the additional destinations
- Return the merged list in a NodeList format that can be used in the custom XSLT
Here is the outline of the DestinationCreationUtility class:

Notice that the DestinationCreationUtility contains the following methods:
public static NodeList appendCreatedDestinations(Node existingDestinationListNode) |
This method will take in an availableDestinations node and will parse those nodes to determine the list of existing destinations. The method will convert the existing destinations into TravelDestination objects to make merging them with the additional destinations list easier.
public static ArrayList<TravelDestination> getAdditionalDestinations() |
This is the method that produces a list of additional destinations. That list and the list of existing destinations can then be merged to create a unique list of TravelDestination objects.
private static NodeList convertToNodeList(ArrayList<TravelDestination> allDestinations) |
This method will take a list of TravelDestination objects and build a NodeList. The convertToNodeList method is implemented as follows:
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xpath.NodeSet;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
public class DestinationCreationUtility {
...
private static NodeList convertToNodeList(
ArrayList<TravelDestination> allDestinations) {
NodeSet resultSet = new NodeSet();
try {
Document doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().newDocument();
for (int i = 0; i < allDestinations.size(); i++) {
TravelDestination next = allDestinations.get(i);
Element nextAvailableDestination = doc.createElement("availableDestination");
Element destinationElement = doc.createElement("destination");
Element cityName = doc.createElement("cityName");
Text cityNameText = doc.createTextNode(next.cityName);
cityName.appendChild(cityNameText);
Element countryName = doc.createElement("countryName");
Text countryNameText = doc.createTextNode(next.countryName);
countryName.appendChild(countryNameText);
Element categoryElement = doc.createElement("category");
Text categoryText = doc.createTextNode(next.category);
categoryElement.appendChild(categoryText);
Element popularityElement = doc.createElement("popularity");
Text popularityText = doc.createTextNode(next.popularity + "");
popularityElement.appendChild(popularityText);
destinationElement.appendChild(cityName);
destinationElement.appendChild(countryName);
nextAvailableDestination.appendChild(destinationElement);
nextAvailableDestination.appendChild(categoryElement);
nextAvailableDestination.appendChild(popularityElement);
resultSet.addElement(nextAvailableDestination);
}
} catch (DOMException e) {
throw new org.apache.xml.utils.WrappedRuntimeException(e);
} catch (ParserConfigurationException e) {
throw new org.apache.xml.utils.WrappedRuntimeException(e);
}
return resultSet;
}
...
}
|
The availableDestinations element does not need to be created because it will be created automatically by the Custom transform, it is only necessary to populate the target elements contents. The final step is to call the appendCreatedDestinations method from the XSLT template. The following sample shows the XSLT template that is used in this example:
<xsl:template name="AvailableDestinationsToAvailableDestinations">
<xsl:param name="availableDestinations"/>
<xsl:variable
name="completeAvailableDestinationList"
select=
"DestinationCreationUtility:appendCreatedDestinations($availableDestinations)"/>
<xsl:copy-of select="$completeAvailableDestinationList"/>
</xsl:template>
|
In the XSLT template, the appendCreatedDestinations method is called and the result is saved in a variable named completeAvailableDestinationList. Next, the value of the variable is copied to the output using the following xsl:copy-of statement:
<xsl:copy-of select="$completeAvailableDestinationList"/> |
In order for the above XSLT to find the DestinationCreationUtility class, we also need to add a namespace declaration to define the location of the class. In this case the namespace declaration would be:
xmlns:DestinationCreationUtility= "xalan://com.ibm.example.travel.util.DestinationCreationUtility" |
If you are unsure how to create this declaration, you can use a Custom Java call in your XML Map file and then copy the declaration from the corresponding .xsl file that is generated for your .map file.
Wildcards are a mechanism that allows an XML Schema definition to be flexible. Because the structure of a wildcard is not defined in the XML Schema, the XML Mapping Editor cannot display a structure for wildcards and therefore mapping to or from a wildcard in the XML Mapping Editor can be difficult. While working in a Mediation flow, one way to avoid having to work with wildcards in the XML Mapping Editor is to use the Set Message Type primitive prior to using the XSL Transform primitive. The Set Message Type primitive will allow you to specify the concrete type that should be used for wildcards.
Based on XML Schema definitions, wildcards can occur in the following three forms:
<any> wildcard
In this case, an element with any name and any type is expected. In the XML Mapping Editor, an <any> wildcard can be recognized by looking for fields using the
icon, the name any, and no specified type in the type column. For example:

In the above example, the <any> wildcard is also repeatable in which case the target can contain an array of elements with any name and type. As with any other field, the cardinality column will indicate whether an <any> wildcard is repeatable or not.
<anyAttribute> wildcard
In this case, zero or mores attributes with any name and any value will be accepted. In the XML Mapping Editor, <anyAttribute> wildcards can be recognized by looking
for fields using the
icon, the name anyAttribute and no specified type in the type column. For example:

anyType type
In this case, an element with the specified name is expected but the element can be of any type. The following is an example of an anyType Type element named correlation shown in the XML Mapping Editor

When the target is expecting an anyType, it is recommended that the type in the resulting target element be set using the xsi:type attribute. The runtime uses the xsi:type attribute while processing and failures may occur if the xsi:type attribute is not set for an anyType target.
Mapping to a wildcard in the target
In cases where you have a target that is a wildcard or has an anyType type, you can use one of the following methods to map into the wildcard target:
Move
<any> wildcard -- If there is a source element which is exactly the element you want to populate the target with, a Move will copy the element from the source to the target <any> wildcard.
When mapping to an <any> wildcard in the target, it is your responsibility to ensure that namespace and processing constraints are not violated. When you select an <any> wildcard in the target column, the Properties view will display the namespace and processing constraints but there is no validation check to ensure the constraints are not violated.
<anyAttribute> wildcard â If the attributes to be included in the target appear with the desired name and values somewhere in the source, you can use one or more Move transforms to copy the attribute(s) from the source to the target. In cases where the attribute does not exist in the source or the attribute name needs to be changed, a Custom transform is required. The following example shows how multiple source attributes could be mapped to an <anyAttribute> wildcard on the target.

anyType type â If there is a source element which is the exact type that you want to use to populate the target, a Move will copy the elements content from the source to the target. The Move transform will automatically add the recommended xsi:type attribute to the target element so that the type is correctly recognized at runtime.
Submap
Use a Submap when you cannot use an exact copy of a source element or attribute. A Submap is useful when working with wildcards because a Submap allows you to specify the output type and therefore see the detailed structure of the output type while editing the Submap.
<any> wildcard -- If you have the data in the source to populate the target but don't have the exact element you want for the target itself, you can use a Submap. When you create the Submap, you set the output element to be the desired element. Within the Submap you will be able to see all the fields of the element you want to build for the target and can map those fields from the corresponding source data.
When mapping to an <any> wildcard in the target, it is your responsibility to ensure that namespace and processing constraints are not violated. When you select an <any> wildcard in the target column, the Properties view will display the namespace and processing constraints but there is no validation check to ensure the constraints are not violated.
<anyAttribute> wildcard â A Submap cannot be used to populate an <anyAttribute> wildcard in the target.
anyType type -- If you have the data in the source to populate the target but don't have an element with the desired type, you can use a Submap. When you create the Submap, you set the output type to be the desired type. Within the Submap you will be able to see all the fields of the target you want to build and can map those fields from the corresponding source data. The Submap transform will use the correct element name for the target element and will also add the recommended xsi:type attribute required to ensure that the target element is correctly recognized at runtime.
Custom
When a Move or a Submap is not applicable, a Custom transform can be used to construct the appropriate element(s) or attribute(s) for the target. If using a Custom transform to map to an element having an anyType type, keep in mind that you should set the xsi:type attribute for that element. An example of using a custom transform to map to an anyType target is explained in the Copying From a Regular Source Array to a SOAP Encoded Target Array section of this article
Mapping from a wildcard in the source
In cases where you want to use data from a wildcard in the source, you can use the following transforms.
Move
<any> wildcard â If you want to make an exact copy of a source element, you can use a Move transform to move the source element represented by the <any> wildcard to the target. The name and type of the source element will need to match what is required by the target in order to be valid at runtime.
<anyAttribute> wildcard â Move cannot be used to map from an <anyAttribute> wildcard source.
anyType type â The Move transform can only be used with a source element having an anyType type if the target element also has an anyType type. Move is not supported from a source element with an anyType type to a target element with a concrete type.
Submap
<any> wildcard -- If you have an <any> wildcard in the source which contains the data you need to populate the target element but does not exactly match the element structure required by the target element, a Submap can be used to map the two elements, or the two types of those elements. When you create the Submap, specify the expected element or type of the source as the Submap input. Once the Submap input is set, the full structure of the input is shown in the Submap and the appropriate source fields can be mapped to the appropriate target fields.
<anyAttribute> wildcard â Submaps are not supported for <anyAttribute> wildcard sources.
anyType type â You can use a Submap refinement to cast the source element to the appropriate type. When you create the Submap, specify the expected source type as the Submap input.
Custom
When a Move or a Submap cannot be used to achieve the desired results, a Custom transform can be used with a wildcard input. A Custom transform will allow you to dissect a source wildcard in any form you want. A Custom transform also allows you to map from two separate wildcard sources into a single target.
When using Custom transforms to map from an <anyAttribute> wildcard, it is recommended that you use the parent of the <anyAttribute> wildcard as the transform
input. This allows you to access the attributes by name or position. For example, if you had a Custom transform with an input from an element named luggage and you wanted to
access an attribute named passengerName to assign to the output attribute, you could use a Custom transform with an XPath expression such as $luggage/@passengerName.
XSD model groups such as xsd:sequence and xsd:choice are not shown in the Simplified view within the XML Mapping editor. Switching the view to Detailed will show the XSD model group information allowing you to identify where choice group elements exist. A drop down menu in the top right corner of the XML Mapping editor allows you to switch between simplified and detailed view.
The choice element cannot be directly mapped, but the elements contained within a choice group can be mapped. An instance document can only contain one of the elements contained within a non repeatable choice model group at any time; not many. Therefore, when creating transforms from members of a non repeatable choice group, only the transforms connected to those member elements which exist at in the runtime instance document will get executed. Any transforms connected to the other elements will be ignored.
When creating transforms which target choice group elements, ensure that the mapping logic only produces one of the choice group elements. Unexpected results will occur if multiple elements in a choice group get set as the generated XSL will try to create a valid XML output document. Adding conditions on transforms, or using Custom transforms to ensure that only one element in a choice groups gets created will ensure that the expected results are achieved during the execution of the map.
Working with substitution groups
Substitution groups are not explicitly shown in the Simplified views, however, you will be able to identify the head elements contained within a substitution group by means
of the following icon on an element:
. Substitution groups are explicitly shown in the Detailed views. To switch between
Simplified and Detailed views, use the drop down menu in the top right corner of the XML Mapping Editor.
Like the actual xsd:sequence and xsd:choice elements that are shown in the Detailed views, substitution group nodes cannot be directly mapped, but the elements contained within a substitution group can be mapped. Similarly, like choice elements, it is important to remember that an instance document can only contain one member of the substitutable elements at a time; not many.
Unlike the Business Object Mapper which bases its mappings on generalizations, the XSLT Mapper will only execute transforms that match the exact element in the instance document during execution. Therefore, creating a "general" transform on the head element of a substitution group to handle any derived element types that may exist in instance document will not handle all cases. Rather, if each element is expected to be transformed, each must be involved in a specific transform to handle the unique case. This method allows certain elements contained in a substitution group to be filtered (by not moving data over to the target) if desired.
When creating transforms which target substitution group members, ensure that the mapping logic only produces one of the members. Unexpected results will occur if multiple elements in a substitution group get set as the generated XSL will try to produce a valid XML output document.
The following section explains some of the common use cases that occur when working with SOAP headers. The value element in the SOAP header element is an anyType type element named value and can be set using the methods explained in the Mapping to a wildcard in the target. The easiest method to work with the value element is to connect it to a Submap transform and use the Submap to create an element of the desired output type.
Another case that can occur is when you want to do a conditional mapping based on the SOAP header name. In this case, you can create the mappings and then apply a condition to the mapping using the properties page for the mapping. You can apply a condition for each condition that needs to be checked against the SOAP header name. For examples of how to do the if-else logic, see Using conditions in Part 1 of this article series.
In cases where your source or target contains a SOAP encoded array, you may encounter a case where you want to move array elements from a SOAP encoded array to a standard array or vice versa. The XML Mapping editor does not recognize the type of the elements within a SOAP encoded array and will display the elements as an array of <any> wildcard elements. The case of converting to or from a SOAP encoded array is covered in this section.
Case 1. Copying from a SOAP-encoded source array to a regular target array
In this case, a For-each and a Submap mapping can be used. The For-each is used to iterate over each of the <any> wildcard elements in the source array and the Submap is then used to cast those elements to the desired type. For example, consider that we have a SOAP encoded array of Destination elements on the source that we want to use to populate a regular array of Destination elements on the target. The map looks like this:

Within the For-each mapping, a Submap mapping is used to map the <any> wildcard elements (which are actually Destination elements in this example) to Destination elements as follows:

The Submap transformation above calls a Submap that has Destination as both the source and target type.
Case 2. Copying rrom a regular source array to a SOAP-encoded target array
The case of going to a SOAP array requires you to write some custom XSLT, which creates the array elements for the SOAP array. The custom XSLT must do the following to create a conforming SOAP array:
- Use the name item for each array element. This will allow the SOAP array contents to be viewed while working in the Integration Test Client. For example:
<item xsi:type="in:Destination">
- Use the xsi:type attribute on each of the items within the SOAP array. Otherwise, a runtime error will occur since the type of the array elements cannot be determined.
<item xsi:type="in:Destination">
To demonstrate the scenario of converting a regular array to a SOAP encoded array, assume that we have a list of Destination elements in the source that we want to use to populate a SOAP encoded list of Destinations on the target. The Custom transform would be created in the map as follows:
The target of the Custom transform is the parent of the repeatable <any> wildcard. This allows us to create the <any> wildcard array elements entirely in the Custom XSLT. The outer shell of the target element is always created by default on a Custom transform so you need to target one level above the element that you need to create. Here is the custom XSLT:
<xsl:template name="DestinationToSOAPEncodedDestinationArray">
<xsl:param name="destination" />
<xsl:for-each select="$destination">
<item xsi:type="in:Destination">
<cityName>
<xsl:value-of select="cityName" />
</cityName>
<countryName>
<xsl:value-of select="countryName" />
</countryName>
</item>
</xsl:for-each>
</xsl:template>
|
Notes:
- The namespace used in the xsi:type value may be a namespace that was created for an input type. If so, the namespace may have been excluded in the following line of code within the custom XSL file:
exclude-result-prefixes="xalan in". Ensure that you remove the prefix from the list of excluded prefixes so that it will be transferred to the output XML document. - There is a known issue with re-used namespaces not being included in the output XML file. The scenario of going from a regular array to a SOAP-encoded array typically produces the problem scenario. For a workaround, see Ensuring a namespace prefix used in Custom XSLT is recognized in output XML.
New target nodes can be introduced by mapping to a target wildcard or to a target element with type anyType. However, new target nodes can also be introduced by mapping to a derived type or alternative substitution group member through the use of a Submap or Custom transform. For some examples, see the Custom section above.
A type mapping cannot be tested as a stand-alone mapping application. It must first be included in a larger global element mapping. As such you can do one of two things to test a type mapping. If the type mapping is already used in a global element mapping, then you can test the type mapping by testing the global element mapping with samples that exercise that particular type mapping. Otherwise, you can create a new wrapper global element mapping, that wrappers the type mapping in a global element mapping that can then be used for testing purposes. You can use existing global elements if available, or create them using new schema files that import the appropriate named types.
For example, suppose that you have the following Submap on type Destination.

Because the above Submap is based on types, it cannot be tested as a stand-alone mapping application. To create a global element to test the above Submap, a new schema definition file would be created as follows:
TestGlobalElement.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://ExtraTestingApplication" xmlns:bons0="http://TravelDestinationsLibrary"> <xsd:import schemaLocation="Destination.xsd" namespace="http://TravelDestinationsLibrary"> </xsd:import> <xsd:element name="DestinationGlobalElement" type="bons0:Destination"> </xsd:element> </xsd:schema> |
Once the above schema definition has been created, a new map can be created based on the global element DestinationGlobalElement defined in the above schema. The new map should use a Submap refinement which references the original DestinationToDestination Submap.
Ensuring a complete target XML document
When you create a map using the XML Mapping Editor, there is no automatic validation to ensure that the mappings you create will produce a complete target XML document. The validation will try to ensure that each target field is populated with valid data, but it will not catch cases where a target field that is required is not populated at all. If a required target field is not mapped, the resulting target element will not be valid according to its schema. A required target field can be identified by looking at the cardinality column for the target. In cases where the cardinality is shown as [1..1] or [1â¦n] or [1â¦*], the field is required. If a field is required, and the parent of that field is present in the output, the required field must also be present in the output. It can be tricky to determine if a field must be present in the output document because it depends on all the fields ancestors. For example, if all the fields ancestors are required, then the field is required. As another example, if the field is required, but the fields parent is optional and is not mapped, the field does not need to present in the output document.
Once you determine that a field is required and must be present in the output XML document, ensure the following:
- There is a mapping to the required field
If the required field is mapped from a mapping with an optional field as the input, ensure that a second conditional mapping is used to cover the case where the source field is not present in the input XML. Basically this is the case where you have used an optional source to populate a required target. In such a case, you should have a contingency plan for cases where the source is not present. To create a contingency mapping, use a condition on whatever mapping you create to ensure that the mapping only happens if the first choice source field is not present in the input. The following example shows how to create a condition that checks for the non-existence of an input:
not(/body/getBestDestinationResponse/response/currentTemperature)
If the above condition was attached to a mapping, the mapping would only happen if the currentTemperature element was not present in the input XML. For more information on using conditions on mappings, see Using conditions in Part 1 of this article series.
Mapping derived types (through extension and restriction)
When mapping derived types, you may notice that some of the extension or restriction information is missing in the XML Mapping Editor. This may affect the validity of the output document, in which case, you will need to use a custom transform to capture the extension or restriction information correctly.
Mappings to target head elements
Occasionally you will find that it is necessary or at least beneficial to map to a target head element which is the top level target element within a map or a nested map. Below are two examples of head elements within their respective maps or nested maps.


Submap and Custom are the two supported transforms for a target head element mapping. On the one hand, a Submap transform is often required when the types of the head elements have already been mapped and are available for reuse. So instead of remapping the content of the head elements, you can just reuse the type mapping. On the other hand, a custom transform may be required, to add namespaces from the source or to add new attributes to the target head element. For an example of a head mapping used on a global element, see Testing a type mapping.
One case where it may be useful to map to the head element is when working with arrays. There may be cases where you have the same type of array on the source and target but want to sort the input array or work with only certain indices of the input array. The logical thing to do would be to create a For-each mapping in order to sort the array or specify the indices, and then perform a Move within the For-each. For example:

Within the For-each:

However, the above example is not valid because a Move was used to map the head element destination. The above example could be fixed by replacing the Move with a Submap.

Ensuring a namespace prefix used in Custom XSLT is recognized in output XML
There is a known problem:
- An XSL file (such as the one generated for a .map file) calls another XSL template (such as a Custom XSLT mapping)
The calling XSL file uses the exclude-result-prefixes attribute to exclude certain prefixes. For example:
xmlns:in="http://TravelDestinationsLibrary" ... exclude-result-prefixes="in xalan"
:
- The called XSL file re-uses the excluded prefix but does not declare it to be excluded. For example:
xmlns:in="http://TravelDestinationsLibrary" ... exclude-result-prefixes="xalan"
- The output XML file will not contain the namespace declaration required by the called XSL file and usage of that namespace will cause errors at runtime because it is not recognized.
The problem will not occur during local map tests nor will it occur at runtime if the server is running in debug mode. The problem only occurs when running the server in non-debug mode. A common scenario where this can happen is when using the xsi:type attribute in Custom XSLT. There is a workaround to ensure the namespace gets included. The workaround is explained using the same example that was used to explain the scenario of mapping from a regular array to a soap encoded array. In this case, Custom XSLT is used and the Custom XSLT uses the xsi:type attribute as follows:
<xsl:template name="DestinationToSOAPEncodedDestinationArray">
<xsl:param name="destination" />
<xsl:for-each select="$destination">
<item xsi:type="in:Destination">
<cityName>
<xsl:value-of select="cityName" />
</cityName>
<countryName>
<xsl:value-of select="countryName" />
</countryName>
</item>
</xsl:for-each>
</xsl:template>
|
The item element is declared with an xsi:type attribute that uses the in namespace as follows:
<item xsi:type="in:Destination"> |
The above XSLT will produce the correct result while testing locally but will fail at runtime if the server is not running in debug mode because the in namespace will not be recognized. To resolve the problem, the namespace declaration is added to the element tag using <xsi:text>
<xsl:template name="DestinationToSOAPEncodedDestinationArray">
<xsl:param name="destination" />
<xsl:for-each select="$destination">
<xsl:text disable-output-escaping="yes">
<item xmlns:in="http://TravelDestinationsLibrary" xsi:type="in:Destination">
</xsl:text>
<cityName>
<xsl:value-of select="cityName" />
</cityName>
<countryName>
<xsl:value-of select="countryName" />
</countryName>
<xsl:text disable-output-escaping="yes">
</item>
</xsl:text>
</xsl:for-each>
</xsl:template>
|
The item elements start and end tags are wrapped with nxsi:text to force the inclusion of the in namespace declaration.
- Participate in the discussion forum.
- XML Path Language (XPath) Version 1.0 W3C Recommendation
- XSL Transformations (XSLT) Version 1.0 W3C Recommendation
- WebSphere Integration Developer developer resources page
Technical resources to help you use the WebSphere Integration Developer IDE to render your existing IT assets as service components, encouraging reuse and efficiency as you build SOA-based integration solutions across WebSphere Process Server, WebSphere ESB, and WebSphere Adapters. - WebSphere Integration Developer product page
Product descriptions, product news, training information, support information, and more. - WebSphere Integration Developer information center
A single Web portal to all WebSphere Integration Developer documentation, with conceptual, task, and reference information on installing, configuring, and using your WebSphere Integration Developer environment. - WebSphere Integration Developer information roadmap
Roadmap of articles and resources to help you with installation, migration, administration, development, troubleshooting, and understanding the underlying technology. - WebSphere Integration Developer documentation library
WebSphere Integration Developer product manuals. - WebSphere Integration Developer support
A searchable database of support problems and their solutions, plus downloads, fixes, problem tracking, and more. - WebSphere ESB developer resources page
Technical resources to help you use WebSphere ESB as a flexible connectivity infrastructure for integrating applications and services to support an SOA. - WebSphere ESB product page
Product descriptions, product news, training information, support information, and more. - WebSphere ESB information center
A single Web portal to all WebSphere ESB documentation, with conceptual, task, and reference information on installing, configuring, and using WebSphere ESB. - WebSphere ESB documentation library
WebSphere ESB product manuals. - WebSphere ESB FAQs
Basic questions and answers about the new WebSphere ESB product and its relationship to other WebSphere products. - WebSphere ESB support
A searchable database of support problems and their solutions, plus downloads, fixes, problem tracking, and more. - WebSphere SOA solutions developer resources page
Get technical resources for WebSphere SOA solutions. - developerWorks SOA and Web services zone
Technical resources for evaluating, planning, designing, and implementing solutions that involve SOA and Web services. - developerWorks WebSphere Business Integration zone
For developers, access to WebSphere Business Integration how-to articles, downloads, tutorials, education, product info, and more. - WebSphere Business Integration products page
For both business and technical users, a handy overview of all WebSphere Business Integration products - WebSphere forums
Product-specific forums where you can get answers to your technical questions and share your expertise with other WebSphere users. - Most popular WebSphere trial downloads
No-charge trial downloads for key WebSphere products. - Technical books from IBM Press
Convenient online ordering through Barnes & Noble. - developerWorks technical events and Webcasts
Free technical sessions by IBM experts that can accelerate your learning curve and help you succeed in your most difficult software projects. Sessions range from one-hour Webcasts to half-day and full-day live sessions in cities worldwide.
Dave Spriet is the Mapping and Transformation Tools Architect at the IBM Toronto Software Lab, Canada. He develops tools for WebSphere Integration Developer and WebSphere Message Broker and specializes in XML and transformation technologies, which consist of XML, XSLT, XQuery, XPath and Java. He has a Bachelor's degree (with Honors) in Computer Science and Statistics from McMaster University, Canada.




