赞
踩
这个由2部分组成的系列文章的第1部分, 使用XML映射编辑器开发地图 ,向您展示了如何使用IBM®WebSphere WebSphere Integration Developer V7.0.0(以下称为Integration Developer)快速创建健壮且组织良好的XML映射。 它还描述了可用于测试和调试地图的工具,以使问题确定更加容易。 本部分(第2部分)向您展示如何执行更复杂的XML映射。
源或目标中的某些字段可能是可重复的,在这种情况下,存在一系列特定元素。 通常,在使用数组时,总是从将输入数组映射到输出数组开始,这将创建一个For-each转换, 如图1所示。
我们不建议您直接映射数组元素的子级,而不在父数组上使用For-each。 例如, 图2不是完成与上述图2相似的映射的推荐方式。
默认情况下,将输入数组连接到For-each转换时,它将导致对输入数组中的所有元素进行迭代,从而将根据For-each映射中定义的嵌套映射来映射每个数组元素。
类似于本地映射,For-each是不会产生任何结果的容器映射,除非有嵌套的映射定义了如何对输入数组的每次迭代进行操作。 因此,您必须指定有关将源中的哪些字段映射到目标中的哪些字段的特定详细信息,以完成For-each实现。 例如, 图3显示了For-each映射内的嵌套映射。
使用数组时,这可能会遇到其他一些情况。
与处理数组的一般情况一样,您仍然要从在源数组元素和目标字段之间创建映射开始。 在这种情况下,该顶级映射将是Local map或Move,因为For-each仅在源和目标是数组时才适用。 在这种特定情况下,您需要确定要使用哪个输入数组元素。 您可以在“属性”页面的“基数”选项卡上为本地地图或“移动”变换设置适用的数组索引。 在“基数”选项卡上指定索引时,索引从1开始而不是0。
为了说明这种情况,请考虑图4中的以下示例以及以下源和目标。
在这种情况下,源包含一组具有城市和国家名称的目标对象,但是目标仅期望一个城市名称和一个国家名称。 在这种情况下,您要获取目标数组的第一个条目,并将其城市和国家/地区名称映射到目标单例。 第一步是在数组和包含要填充字段的目标项目之间创建一个Local映射,如图5所示。
创建该本地映射后,设置基数以指示第一个数组条目将是本地映射转换的输入。 在Local map中,destination元素的字段与GetWeather元素的适当字段匹配,如图6所示。
在这种情况下,假设目标数组仅包含一个元素,则再次需要在源字段和目标数组之间创建一个Local map。 在“本地”地图中,填写有关源地图中哪些字段到目标中哪些字段的详细信息。
您可以通过创建指向数组的多个本地映射并在“属性”页面的“基数”选项卡上指定不同的索引来填充目标数组中的多个元素。 您还可以使用“合并”转换来使用源中两个或多个不同单例中的信息来填充目标中的数组元素。 在“基数”选项卡上指定输出数组索引时,索引从1开始而不是0。
在某些情况下,您可能要在源中使用两个或多个数组来在目标中创建单个数组。 在这种情况下,您可能需要两个结果。 一个结果可能是合并输入数组中的条目以形成目标数组的单个条目。 另一个期望的结果可能是将输入数组中的条目附加在一起以形成更长的输出数组。 合并和附加情况都在下面说明。
情况1-合并
在这种情况下,您要将源中的两个阵列合并到目标上的单个阵列中。 例如,考虑源包含两个单独的数组的情况:名称数组和电话号码数组。 假设电话号码列表与名称列表一一对应,并且您的输出数组包含需要名称和电话号码的元素,则可以将两个单独的数组中的信息合并到目标上的单个数组中侧。
在这种情况下,可以使用“合并”变换来完成所需的结果。 与For-each相似,Merge转换是一个容器映射,它不会产生任何结果,除非有嵌套的映射定义了如何对输入数组的每次迭代进行操作。 因此,您必须指定有关将源中的哪些字段合并到目标中的哪些字段的详细信息,以完成“合并”实现。 注意在合并中如何执行迭代:该过程开始于获取所有输入数组的第一个索引并将这些字段合并到目标数组的第一个索引中。 然后,获取所有输入数组的第二个索引以生成目标数组的第二个索引,依此类推。 一种简单的合并方法是,如果您将两个相同大小的数组合并在一起,则会得到与原始数组大小相同的目标数组。 图7显示了一个示例。
如果合并转换的输入数组的大小不同,则可以在“基数”属性页面上指定要迭代的输入。 默认情况下,迭代将设置为合并变换的第一个输入。 但是,指定当输入数组的大小不同时要迭代的输入数组将在目标数组上产生不同的结果。 以图8和图9中的以下两个示例为例,说明已知输入数组大小不同时的区别。
在以上示例中,生成的目标数组大小直接取决于选择要迭代的输入数组。 在映射执行期间已知输入数组大小相同的情况下,选择要迭代的输入无关紧要,因为会产生相同的结果。
情况2-追加
在这种情况下,您要从一个源数组中获取所有元素,并从另一源数组中获取所有元素,并在目标端创建一个包含两个源数组中所有元素的数组。 例如,假设您在源上有两个旅行目的地列表,并且想在目标上创建一个包含所有目的地的列表。
在这种情况下,您可以使用Append变换完成所需的结果。 考虑追加的一种简单方法是,如果采用两个任意大小的数组,并将它们附加在一起,则会得到目标数组,该数组是输入数组大小的总和。 图10显示了一个示例。
与For-each相似,Append转换是一个容器映射,除非有嵌套的映射定义如何对输入数组的每次迭代进行操作,否则它不会产生任何结果。 因此,您必须指定有关如何将源中的字段添加到目标中的字段以完成Append实现的详细信息。 实施追加转换时,将使用For-each和Move映射自动为您生成嵌套映射的第一层。 在嵌套的For-each映射中,需要进一步的映射才能完成实现。
通过获取第一个输入数组的第一个索引以在目标数组中产生第一个索引,在追加中执行迭代。 然后,采用第一输入数组的第二索引来生成目标数组的第二索引。 此过程将继续进行,直到对第一个输入的所有元素都进行了操作,然后再处理(逐个)第二个输入数组的所有元素,依此类推。 输入数组的附加顺序基于它们进入Append变换的顺序。 如果需要,可以在“追加”变换的“订单属性”页面中对此进行修改。
使用源数组作为转换的输入时,可以选择在发生任何映射之前对输入数组元素进行排序。 可以使用For-each转换的属性页来完成。 您可以在数组中指定要用于排序的字段。 您还可以指定将使用词法搜索还是数字搜索,以及对升序还是降序进行排序。 例如,假设您有一个目的地数组,其中每个目的地都有一个城市名称和一个国家名称。 假设您要基于城市名称按升序对列表进行排序。 您可以使用For-each转换的Properties页面来指定排序, 如图11所示。
有时在处理数组输入时,您可能只想处理数组中的特定元素。 在这种情况下,您可以在“属性”视图的“基数”选项卡上指定适用的数组索引,也可以将过滤器表达式应用于转换。
基数
您可以使用基数来确定哪些数组索引将用作转换的输入。 您可以指定单个索引,索引范围或逗号分隔的索引列表。 表1显示了可接受的特殊字符。
字符 | 含义 |
---|---|
: | 用于指定值的范围。 |
, | 分隔符以分隔索引和/或范围的列表 |
* | 所有索引/直到数组结尾 |
表2显示了如何使用上述特殊字符指定基数的一些示例。
值 | 含义 |
---|---|
1个 | 仅元素1 |
1:3 | 要素1至3 |
2:* | 元素2及以上 |
1,3,5:* | 元素1、3、5及以上 |
数组索引从1而不是0开始。
使用XPath表达式进行过滤
在使用输入数组时,可以在转换中使用过滤器表达式来从源数组中过滤所需的元素。 在这种情况下,过滤器表达式确定从源数组中选择了哪些元素。 编写过滤器表达式以返回供For-each操作的节点集。 为了返回适当的节点集,将过滤器表达式写为谓词而不是true或false条件。 例如,假设您有一个旅行目的地数组,其中每个目的地都有一个城市名称和一个国家名称。 假设您要消除国家名称等于北极的任何目的地。 在这种情况下,可以将过滤器表达式添加到For-each转换中, 如图12所示。
所有未设置为北极的目的地均会将其作为“每个”映射的输入。 设置为北极的目的地将被忽略,并且不属于For-each映射输入的一部分。
组优化为将数组转换为嵌套数组提供了支持。 例如,假设您有一个旅行目的地数组,列表中的每个目的地都有一个类别,以指示旅行目的地的类型。 类别可能包括海滩,冒险等等。 如果要从这些目标创建嵌套数组,其中类别构成顶级数组,而每个类别中的目标构成嵌套数组,则可以使用组转换。 图13显示了Group转换,将目的地按类别分组到一个嵌套数组中。 请注意,在图13的“属性”页面中的“组”转换中,类别字段被指定为分组条件。
类似于本地映射,组是容器映射,除非存在定义如何对输入数组的每次迭代进行操作的嵌套映射,否则它不会产生任何结果。 因此,必须指定有关将源中的哪些字段映射到目标中的哪些字段的详细信息,以完成Group的实现。
当前没有任何转换可让您将嵌套数组取消分组为单个数组。 但是,您可以使用自定义XSLT转换将嵌套数组操纵为单个数组。 有关如何完成此操作的示例,请参见本文的“ 定制XSLT”部分。
在某些情况下,您可能会发现没有适当的现成优化方案来执行正确填充目标所需的数据操作。 在这种情况下,您可以使用自定义转换。 以下各节说明了三种定制转换类型:定制XPath,定制XSLT和定制Java™。
在某些情况下,可能需要使用XPath表达式或XPath表达式的组合,这些表达式不能作为内置函数优化使用。 在这种情况下,您可以编写自定义XPath表达式以获得所需的结果。 在编写XPath表达式时,自定义转换的输入可用作变量。 您可以使用以下语法引用它们: $<variablename>
。
例如,如果变换具有一个名为currentTemperature的输入元素,则可以使用$currentTemperature
引用该元素。
有关在表达式中使用XPath运算符的示例,请考虑以下情形:要使用xsd:int类型的两个输入的总和来产生xsd:int结果。 使用连接到该转换的两个int输入创建一个Custom转换,然后使用如下表达式将两个输入相加: $inputx + $inputy
。
在XPath表达式输入字段中调用content-assist(使用CTRL + Space)将显示可用变量的列表以及可以轻松插入表达式的XPath函数的列表, 如图14所示。
您可以使用XSLT实现自定义转换。 使用XSLT时,将调用外部文件中的命名模板以产生目标输出。 自定义转换的输入作为参数发送到自定义XSLT。 常见的XSLT模板存储在库中,并由许多映射共享。 自定义XSLT对于在目标中创建源中不存在的元素很有用。 通常,您可以使用Submap来完成此操作,但是在某些情况下,Submap无法使用。 使用Custom XSLT时要记住的一些事情是:
作为使用自定义XSLT的示例,请考虑源上存在嵌套数组的情况。 如图15所示,外部数组是类别的列表,每个类别都包含与类别匹配的旅行目的地的列表。 该图使用以下样本数据:
上面的定制转换将调用下面的XSLT模板,将每个类别传递给模板,如清单1所示。
- <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>
该模板使用xsl:for-each从每个类别中提取目的地,如清单2所示。
<xsl:for-each select="$category/travelDestination">
该模板还基于嵌套数组的父类别在输出目标中设置类别值,如清单3所示。
<category><xsl:value-of select="../categoryName"/></category>
给定样本输入数据,清单4中显示了availableDestinations输出数组。
- <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>

要继续该示例,请假定您实际上并不想将所有目的地类别都放到一个列表中,而是只想从一个类别中将目的地放到一个列表中。 让我们假设客户将让您知道他们感兴趣的目的地类别。 图16中的下图显示了客户选择的旅行类别以及已传递到Custom XSLT转换中的归类目的地列表。
上面的地图调用以下XSLT模板,仅从适用的类别中查找目的地,如清单5所示。
- <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>
在上面的示例中,请注意以下几点:
<xsl:if test="../categoryName=$customerTravelCategory">
注意 :每次为自定义XSLT引用XSLT文件时,都会将该XSLT文件的导入添加到映射文件中。 如果要将XSLT文件添加和删除到特定地图,则可能值得使用“属性”页面中的“ XSLT导入”选项卡清理未使用的导入。
在XSLT中使用内置扩展
XSL Transformation原语使用的XSLT处理器在WebSphere Process Server和WebSphere Enterprise Server Bus运行时中运行时,基于Apache Xalan-Java XSLTC处理器,该处理器提供EXSLT扩展功能,包括字符串函数,数学函数,设置函数,和日期和时间功能。 有关EXSLT函数的列表,请参见Apache XML Project 。
在某些情况下,IBM XSLTC处理器不同于Apache Xalan-Java XSLTC处理器,因此,上述所有功能可能都不可用。 以下示例显示了自定义XSLT模板中使用的ExstlString:split函数。 在该示例中,GetWeather服务返回了一个XML字符串,该字符串中的某处有一个要提取的温度值。 例如:
- ...
- ...
- <Temperature> 55 F (13 C) </Temperature>
- ...
- ...
- ...
- 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>

注意:
xmlns: str ="http://exslt.org/strings"
。 select="str:split($GetWeatherResult, '<Temperature>')[2]"
。 使用库中的通用XSL文件
我们建议您将常见的XSL模板放在库项目中的自定义XSL文件中。 通过使用库,多个中介模块可以访问通用XSL模板。
将公共XSL文件导入位于中介模块中的XSL文件时,请使用xsltcl URI。 例如,如果中介模块中的XSL文件是MediationModule1 / xslt / MyMap-custom.xsl,而您要导入的XSL文件是/Library1/common/CommonXSLLibrary.xsl,则导入将是: <xsl:import href="xsltcl://common/CommonXSLLibrary.xsl"/>
。
URI中不包含库的名称Library1。
实现自定义转换的第三种方法是使用对静态Java方法的调用。 您可以使用Java调用来填充任何简单类型或复杂元素的值。 您可以将输入连接到Custom Java转换,并将这些输入用作方法参数。 输入可以是简单或复杂的类型。 当使用复杂类型输入作为方法参数时,相应的方法期望复杂类型数组的参数类型为org.w3c.dom.Node或org.w3c.dom.NodeList。 使用定制Java转换时,可以使用“定制”转换的“属性”页面选择所需的类和方法,以及将“定制”转换的输入映射到Java方法的参数。
为了说明Custom Java调用的基本用法,请考虑清单9中所示的示例。在中介流中,将进行对Web服务的调用,以检查特定目的地的天气。 气象服务返回XML结果,而目的地的温度被埋在该结果中。 静态Java方法用于解析XML数据,并且仅提取温度作为int值。
- <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>
假设您有兴趣提取温度的摄氏温度版本(对于上面的示例是21)。给定上述数据,清单10中所示的以下Java方法将通过在输出中找到<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;
-
- }

若要在自定义转换中使用上述方法,请执行以下操作:
在前面的示例中,Custom Java转换的输入和输出都是简单类型。 作为另一个示例,请考虑您要使用复杂类型输入数组在目标上填充复杂类型数组的情况。 Java方法将NodeList作为输入参数,并返回NodeList作为结果。
在此示例中,假设您在源端具有一个目标数组,并且想要在填充目标端的目标列表之前使用Java方法向列表中添加一些其他目标。 第一步是创建一个自定义Java类,它将执行以下操作:
在此示例中,将创建DestinationCreationUtility类。 该类将包含清单11中所示的以下方法。
- public static NodeList appendCreatedDestinations(NodeList
- arrayOfTravelDestinations)
此方法将接受TravelDestination节点的数组,并将解析这些节点以确定现有目的地的列表。 该方法会将现有的目的地转换为TravelDestination对象,以更轻松地将其与其他目的地列表合并,如清单12所示。
public static ArrayList<TravelDestination> getAdditionalDestinations()
这是产生其他目的地列表的方法。 然后,您可以合并该列表和现有目的地列表,以创建TravelDestination对象的唯一列表,如清单13所示。
private static NodeList convertToNodeList(ArrayList<TravelDestination> allDestinations)
此方法将获取TravelDestination对象的列表并构建一个NodeList。 如清单14所示实现了convertToNodeList方法。
- 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;
- }
- ...
- }

为了完成任务,一旦创建了DestinationCreationUtility类,就创建了一个使用Custom Java变换将源目的地映射到目标目的地的映射, 如图19所示。
调试自定义Java调用
当具有使用自定义Java调用的映射时,可以调试该映射在服务器上运行时正在调用的Java。 使用“测试图”视图或使用Integration Test Client在本地测试图文件时,在Java代码中设置断点将没有任何影响。 但是,如果您正在使用Integration Test Client进行组件测试,并且服务器正在Debug模式下运行,则您在称为Java方法中设置的断点将导致执行停止,因此您可以逐步执行Java方法。
通配符是一种允许XML模式定义灵活的机制。 由于通配符的结构未在XML模式中定义,因此XML映射编辑器无法自动显示通配符的结构。 从V7.0.0开始,XML映射编辑器提供了将通配符转换为具体类型的支持,从而可以显示结构并轻松进行映射。 在中介流中工作时,进行转换的另一种方法是在使用XSL转换原语之前使用“设置消息类型”原语。 设置消息类型原语将允许您指定用于通配符的具体类型。
基于XML架构定义,通配符可以以以下三种形式出现:
<any>通配符
在这种情况下,期望具有任何名称和任何类型的元素。 在XML映射编辑器中,通过使用
在上面的示例中,<any>通配符也是可重复的,在这种情况下,目标可以包含具有任何名称和类型的元素数组。 与任何其他字段一样,基数列将指示<any>通配符是否可重复。
<anyAttribute>通配符
在这种情况下,将接受具有任何名称和任何值的零个或多个属性。 在XML映射编辑器中,<anyAttribute>通配符通过使用
anyType类型
在这种情况下,应使用具有指定名称的元素,但是该元素可以是任何类型。 以下是图22的XML映射编辑器中显示的名为related的anyType Type元素的示例。
当目标需要anyType时,建议使用xsi:type属性设置结果目标元素中的类型。 运行时在处理时使用xsi:type属性。 如果未为anyType目标设置xsi:type属性,则可能会发生故障。
如果您的源是通配符或具有anyType类型,则可以使用以下方法之一将输入的内容映射到目标:
投
<any>通配符 :如果您可以将潜在的<any>通配符输入的范围缩小到一组全局元素,则可以将输入<any>通配符转换为每个潜在的全局元素。 转换后,只有在运行时相应的全局元素实际上是输入文档的一部分时,才会运行从转换的全局元素创建的转换。
<anyAttribute>通配符 :如果您可以将潜在的<anyAttribute>通配符输入的范围缩小到一组全局属性,则可以将输入的<anyAttribute>通配符转换为每个潜在的全局属性。 转换后,只有在运行时相应的全局属性实际上是输入文档的一部分时,才会运行从转换的全局属性创建的转换。
anyType类型 :如果您可以将潜在输入类型的列表缩小为一组具体类型,则可以将anytType输入元素强制转换为每种潜在具体类型。 转换后,只有在运行时相应类型实际上是输入文档的一部分时,才会运行从转换类型创建的转换。
移动
如果不需要操纵输入,则可以使用“移动”变换将精确的输入复制到目标文档。
<any>通配符 :如果要精确复制源元素,则可以使用Move转换将<any>通配符表示的源元素移动到目标。 为了在运行时有效,源元素的名称和类型将需要与目标所需的名称和类型匹配。
<anyAttribute>通配符 :您不能使用Move从<anyAttribute>通配符源进行映射。
anyType类型 :如果目标元素也具有anyType类型,则只能将Move变换与具有anyType类型的源元素一起使用。 不支持从具有anyType类型的源元素移动到具有具体类型的目标元素。
子图
如果确定所需强制转换的逻辑复杂且可恢复,请考虑使用Submap。
<any>通配符 :如果源中有一个<any>通配符,其中包含填充目标元素所需的数据,但与目标元素所需的元素结构不完全匹配,则可以使用Submap进行映射这两个元素,或这些元素的两种类型。 创建Submap时,将期望的元素或源类型指定为Submap输入。 设置子图输入后,输入的完整结构将显示在子图中,并且适当的源字段将映射到适当的目标字段。
<anyAttribute> wildcard : Submaps are not supported for <anyAttribute> wildcard sources.
Custom
When a Cast, Move, or a Submap cannot be used to achieve the desired results, you can use a Custom XPath, Custom XSLT, or Custom Java transform with a wildcard input. A Custom transform allows 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 XPath 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 XPath transform with an XPath expression such as $luggage/@passengerName
.
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.
Cast
<any> wildcard : Casting allows you to cast an <any> wildcard to one or more specific global elements. If you are certain that the same global element or elements will always be used to populate the target <any> wildcard, you can cast the <any> wildcard to each of the expected global elements. When the target is expecting only a single element and there are multiple cast global elements, we recommend that you create mappings at the top level of the cast global elements. For example, consider the case where a singleton <any> wildcard is cast to two different global elements, GlobalElementA and GlobalElementB. The intention is that, depending on the existence of certain inputs, only one of GlobalElementA or GlobalElementB is created in the target at runtime. To ensure that only one of the global elements is created at runtime, ensure the following:
Figure 23 illustrates mapping to the top most level, the following mappings are created at the top most level of the cast global elements and is correct.
The following mappings are not created at the top most level and lead to empty elements being created at runtime, regardless of the existence of the input elements. The following is incorrect, shown in Figure 24.
<anyAttribute> wildcard : Casting allows you to cast an <anyAttribute> wildcard to a one or more specific global attributes. If you are certain that the same global attributes will always be used to populate the target <anyAttribute> wildcard, you can cast the <anyAttribute> wildcard to the each of the expected global attributes.
anyType type : If the type of the target anyType is known, the preferred method for working with the anyType is to cast it to a concrete type.
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 attributes 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. Figure 25 shows an example how multiple source attributes are 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 populate the target, a Move will copy the elements content from the source to the target. The Move transform automatically adds the recommended xsi:type attribute to the target element so that the type is correctly recognized at runtime.
Submap
In cases where you have complex logic that determines what types are used and you want that logic to be reusable, you can use a Submap.
<any> wildcard : When mapping to an <any> wildcard in the target using a Submap, 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 that the constraints are not violated.
<anyAttribute> wildcard : You cannot use a Submap cannot to populate an <anyAttribute> wildcard in the target.
anyType type : When used with anyType type targets, a Submap transform uses the correct element name for the target element and also adds the recommended xsi:type attribute required to ensure that the target element is correctly recognized at runtime.
Custom
When a Cast, Move transform, or Submap transform is not applicable, you can use a Custom XPath, Custom XSLT, or Custom Java transform to construct the appropriate elements or attributes for the target. If using a Custom transform to map to an element having an anyType type, keep in mind that you need to set the xsi:type attribute for that element.
In cases where a base type has been extended or restricted, you can use casting to display the derived structure in the source or target. In the case where the input contains a base type of a derived type, you can cast that base type to any of its derived types as shown in Figure 26 .
In the above example, Address is the base type and is the type that is defined as the type for field2 in the schema. In this case, field2 has been cast to both USAddress and UKAddress, which are both extended types of type Address.
To prevent undesirable duplicate or empty elements from being created in the target at runtime, we recommend that you follow the following conventions:
Casting is done by right-clicking a field in the source or target and selecting the Cast menu action. Only valid derived types are shown when you invoke the casting action. If there are no valid derived types for the selected field, a dialog is displayed to indicate no types are available. If the desired types are not found, ensure that the desired derived types are included in a referenced project.
XSD model groups, such as xsd:sequence and xsd:choice, are not shown by default within the XML Mapping editor. Changing the view preferences to show groups will show the XSD model group information, allowing you to identify where choice group elements exist. Click the Open preferences button (
You cannot directly map the choice element, but you can map the elements contained within a choice group. An instance document can only contain one of the elements contained within a non repeatable choice model group at any time; 不太多。 Therefore, when creating transforms from members of a non-repeatable choice group, only the transforms connected to those member elements, which exist in the runtime instance document, will get executed. Any transforms connected to the other elements are 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. Using If, Else If, and Else transforms, or using Custom XPath, Custom XSLT, or Custom Java transforms to ensure that only one element in a choice group gets created will ensure that the expected results are achieved during the execution of the map.
Substitution groups are not explicitly shown by default in the XML Mapping Editor. However, you can identify the head elements contained within a substitution group by means of the following icon on an element:
Like the actual xsd:sequence and xsd:choice elements that are shown, you cannot directly map sbstitution group nodes, but you can map the elements contained within a substitution group. 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; 不太多。
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 section. The easiest method to work with the value element is to cast it to 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 If, Else If and Else mappings. For examples of how to do the if-else logic, see Conditional mapping in Part 1 of this 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.
In this case, you can use a For-each and a Submap mapping. 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 you have a SOAP encoded array of Destination elements on the source that you want to use to populate a regular array of Destination elements on the target as shown in Figure 29 .
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 shown in Figure 30 .
The Submap transformation above calls a Submap that has Destination as both the source and target type.
The case of going to a SOAP array requires writing custom XSLT to create the array elements for the SOAP array. The custom XSLT must do the following to create a conforming SOAP array:
< item xsi:type="in:Destination">
. <item xsi:type ="in:Destination">
. To demonstrate the scenario of converting a regular array to a SOAP encoded array, assume that you have a list of Destination elements in the source that you want to use to populate a SOAP encoded list of Destinations on the target. The Custom transform is created in the map as shown in Figure 31 .
The target of the Custom XSLT transform is the repeatable <any> wildcard. On the Custom XSLT Properties page, the Include target element within the template checkbox is checked, which allows you 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 XSLT transform, unless you select the Include target element within the template checkbox. Listing 15 shows the custom XSLT.
- <xsl:template name="DestinationToSOAPDestination">
- <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>
New nodes can be introduced into the target in a number of ways. One such way is to apply a Submap or Custom transform to a target node that is replaceable (for example, a wildcard, a derivable type, or a substitution group member). You can then replace that target node with an appropriate node or set of nodes in the specific Submap or Custom code. Alternatively, you can introduce new nodes directly into the mapping editor by substituting one substitution group member for another, or by casting a base type to one or more of its derived types. For more information, see the Custom and Working with types defined through extension and restriction sections.
When you create a map using the XML Mapping Editor, there is no automatic validation to ensure that the mappings you created will produce a complete target XML document. The validation tries to ensure that each target field is populated with valid data, but it does 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 is not valid according to its schema. You can identify a required target field 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:
Occasionally, you will find that it is necessary, or at least beneficial, to map to a target head element that is the top level target element within a map or a nested map. Figure 32 and Figure 33 are two examples of head elements within their respective maps or nested maps.
Mappings to a head element are not supported in all cases. The following are general rules for mapping to head element targets:
There is a known problem:
- xmlns:in="http://TravelDestinationsLibrary"
- ...
- exclude-result-prefixes="in xalan"
- xmlns:in="http://TravelDestinationsLibrary"
- ...
- exclude-result-prefixes="xalan"
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 shown in Listing 16.
- <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 shown in Listing 17.
<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>, as shown in Listing 18.
- <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.
There is also a properties page called Namespaces, which allows for custom prefixes to be defined for a given namespace. You can use this in cases where prefixes are not bound to a specific namespace at runtime, or if there is a need to use a constant prefix throughout the application for a given namespace.
In this article, you learned how to work with complex arrays, custom transformations with Java, XSLT templates and XPath, along with best practices for handling XML schema any, anyType and derived types. The article also discussed how to handle SOAP headers and also SOAP encoded arrays within WebSphere Enterprise Service Bus. These key mapping skills are an important part in creating more complex XMl maps within WebSphere Integration Developer.
翻译自: https://www.ibm.com/developerworks/websphere/library/techarticles/1003_spriet2/1003_spriet2.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。