内容


WebSphere Transformation Extender 的 15 个开发技巧和诀窍

Comments

简介

本文适合那些具有良好的概念性编程知识的开发人员,帮助他们了解 IBM® WebSphere® Transformation Extender(以下简称 WebSphere TX)的一些常用功能并快速地应用它们完成现实的转换任务。大多数问题与递归结构以及如何处理动态输入和输出数据有关。最后的示例讨论如何组合使用多种技术解决比较复杂的转换问题。右边的内容目录列出这 15 个技巧和诀窍。

1. 如何在输出结构中产生固定次数的重复

为了简单,本文中的所有示例都使用 XML 消息。直接使用 XML 模式定义输入和输出结构,而不是使用 WebSphere TX 类型树。本文中讨论的大多数场景都处理输出结构中动态(可变)次数的重复,但是在讨论这些比较复杂的情况之前,先通过第一个示例介绍如何生成包含预定义的固定次数重复的输出结构:

清单 1. Map_FixedRepeats 的输入消息
<InputMessage>
     <Field>some_data</Field>
</InputMessage>

清单 1 所示的输入消息提供一个数据值 some_data,它与一个序号连接起来生成每个输出元素。为了实现映射,右键单击输出树中的重复元素并在菜单中选择 Add Index,见图 1。对于每个重复事件重复该操作,这会在 WebSphere TX 映射的输出卡中生成单独元素,可以给这些元素分配单独的值。

图 1. 在重复字段中添加索引
在重复字段中添加索引
在重复字段中添加索引

清单 2 给出这个映射产生的 XML 输出消息。OutputField 总共出现 5 次,这由 XML 模式定义控制(见 WebSphere TX 项目 Map_FixedRepeats 中包含的 OutputSchema.xsd 文件),XML 模式定义包含代码 <xs:element ref="OutputField" minOccurs="5" maxOccurs="5"/>

清单 2. Map_FixedRepeats 的输出消息
<OutputMessage>
     <OutputField>1st_some_data</OutputField>
     <OutputField>2nd_some_data</OutputField>
     <OutputField>3rd_some_data</OutputField>
     <OutputField>4th_some_data</OutputField>
     <OutputField>5th_some_data</OutputField>
</OutputMessage>

2. 了解一个功能性映射要执行多少次

WebSphere TX 把所有映射分为功能性的和可执行的两类。顾名思义,可执行的映射是用于运行映射的入口点,它们提供 WebSphere TX Design Studio 的 Composition 视图所示的映射层次结构中的顶层。图 2 显示一个名为 Map_Functionals 的可执行映射,它使用功能性映射 FuncMap

图 2. Composition 视图中显示的 WebSphere TX 映射 Map_Functionals 的映射层次结构
Composition 视图中显示的 WebSphere TX 映射 Map_Functionals 的映射层次结构
Composition 视图中显示的 WebSphere TX 映射 Map_Functionals 的映射层次结构

功能性映射提供可重用的代码,可以在一个或多个映射规则中像子例程一样调用它们。功能性映射的用途是接收一个或多个输入对象并生成单一输出对象。在映射规则中,为功能性映射指定输入值的次序并不影响调用映射的次数,但是会影响输出对象出现的次序。例如,请考虑清单 3 所示的输入消息,其中一共包含 6 个简单的元素。OneRepeat 元素出现一次,TwoRepeats 出现两次,ThreeRepeats 出现三次。

清单 3. Map_Functionals 的输入消息
<InputMessage>
     <OneRepeat>A</OneRepeat>
     <TwoRepeats>B</TwoRepeats>
     <TwoRepeats>C</TwoRepeats>
     <ThreeRepeats>D</ThreeRepeats>
     <ThreeRepeats>E</ThreeRepeats>
     <ThreeRepeats>F</ThreeRepeats>
</InputMessage>

图 3 显示对功能性映射的调用。有三个参数,每个参数定义一个值序列:

  • 第一个序列(源自 OneRepeat)只包含一个值 {A}
  • 第二个序列(源自 TwoRepeats)包含两个值 {B,C}
  • 第三个序列(源自 ThreeRepeats)包含三个值 {D,E,F}
图 3. Map_Functionals 中的映射规则,在对 FuncMap 的调用中有三个参数
Map_Functionals 中的映射规则,在对 FuncMap 的调用中有三个参数
Map_Functionals 中的映射规则,在对 FuncMap 的调用中有三个参数

如果出现调用的参数所指定的输入对象,就触发功能性映射。每次运算输入对象的新组合时,都执行功能性映射。WebSphere TX 首先遍历功能性映射调用的第一个参数所指定的值序列,从而生成重复的 OutputElement 实例。然后,WebSphere TX 遍历第二个参数所指定的值序列,从而生成下一组实例。最后,处理第三个参数。因此,如清单 4 所示,输出 XML 中 OutputElement 值的次序是 ABDACDABEACEABFACF

清单 4. Map_Functionals 的输出消息
<OutputMessage>
     <OutputElement>ABD</OutputElement>
     <OutputElement>ACD</OutputElement>
     <OutputElement>ABE</OutputElement>
     <OutputElement>ACE</OutputElement>
     <OutputElement>ABF</OutputElement>
     <OutputElement>ACF</OutputElement>
</OutputMessage>

3. 在没有重复输入结构的情况下,如何生成重复的输出结构

为了在输出消息中创建重复的字段,首先要查看映射的输入消息,寻找重复所需次数的字段。在真实的场景中,输出结构的基数通常等于输入结构的基数。例如,假设一个映射的输入是订单,输出是发票,发票中列出的物品数量很可能与订单中列出的物品数量匹配。但是,在一些比较少见的场景中,没有重复所需次数的合适的输入结构。在这些场景中,映射开发人员可以使用本节介绍的技术。请考虑清单 5 中的输入消息,其中包含 DesiredRepeats 元素,它的指定在输出消息中应该生成 Repeat 元素多少次:

清单 5. Map_GeneratingRecursion 的输入消息
<InputMessage>
     <DesiredRepeats>7</DesiredRepeats>
     <DesiredValue>FixedValue</DesiredValue>
</InputMessage>

为了实现所需次数的重复,用一个使用 CLONE 函数的参数驱动功能性映射调用,见图 4。这个函数创建一个对象的多个拷贝(重复的次数由 CLONE 的第二个参数指定)。CLONE 的第一个参数指定产生的输出序列的每个成员应该计算的值:

图 4. Map_GeneratingRecursion 中的映射规则
Map_GeneratingRecursion 中的映射规则
Map_GeneratingRecursion 中的映射规则

这个映射生成的输出消息如下所示:

清单 6. Map_GeneratingRecursion 的输出消息
<OutputMessage>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
     <Repeat>FixedValue</Repeat>
</OutputMessage>

4. 如何从输入范围中选择单一值

可以使用 CHOOSE 函数从输入范围中选择单一值,它根据值在序列中的位置做出选择。CHOOSE 函数有两个参数:

  • 将从其中选择对象的序列对象的名称
  • 表示想要的对象在序列中的索引的整数(或计算出整数的表达式)

为了演示如何使用这个函数,假设输入消息包含三个 Field 元素:

清单 7. Map_Choose 的输入消息
<InputMessage>
     <Field>some_data1</Field>
     <Field>some_data2</Field>
     <Field>some_data3</Field>
</InputMessage>

如图 5 所示,映射 Map_Choose 的输出卡使用了 CHOOSE 函数,它选择输入消息中 Field 元素的第二个实例:

图 5. Map_Choose 中的输出卡
Map_Choose 中的输出卡
Map_Choose 中的输出卡

这个映射生成的输出消息如下所示:

清单 8. Map_Choose 的输出消息
<OutputMessage>
     <OutputField>some_data2</OutputField>
</OutputMessage>

5. 如何从输入范围中提取一组值

这个示例解释如何在 WebSphere TX 映射规则中使用 EXTRACT 函数选择一组值。EXTRACT 函数用于生成包含满足选择条件的多个对象的结果集。可以把这个函数看作相当于带 WHERE 子句的 SQL SELECT 语句。EXTRACT 函数是选择一组值的非常高效的方法,但是如果预期的结果集只是单一对象,那么最好使用前面介绍的 CHOOSE 函数。用于演示这个函数的输入消息示例见清单 9。Field 元素的每个实例包含两个子元素 —— NameAddress

清单 9. Map_Extract 的输入消息
<InputMessage>
     <Field><Name>Ben Thompson</Name><Address>Hursley</Address></Field>
     <Field><Name>Michael Hudson</Name><Address>Boca Raton</Address></Field>
     <Field><Name>Anthony ODowd</Name><Address>Hursley</Address></Field>
     <Field><Name>Lew Schliesman</Name><Address>Boca Raton</Address></Field>
</InputMessage>

EXTRACT 函数有两个参数:

  • 将从其中选择对象的序列对象的名称
  • 一个条件表达式。函数会提取导致条件表达式计算结果为 true 的所有序列对象成员
图 6. Map_Extract 中的映射规则
Map_Extract 中的映射规则
Map_Extract 中的映射规则

图 6 所示的映射会提取一组与值为 HursleyAddress 元素相关联的 Name 元素。清单 10 中的输出消息表明选择了 Ben ThompsonAnthony ODowd

清单 10. Map_Extract 的输出消息
<OutputMessage>
     <OutputField>Ben Thompson</OutputField>
     <OutputField>Anthony ODowd</OutputField>
</OutputMessage>

6. 如何检查一个值是否是一个组的成员

可以使用 MEMBER 函数判断一个对象是否在一个序列中出现,这个函数返回 "true" 或 "false"。这个示例演示如何使用这个函数检查输入值是否是预定义的允许值序列的成员,然后根据结果产生不同的输出值。清单 11 所示的输入消息示例可以包含一个或多个 Address 元素。

清单 11. Map_Member 的输入消息
<InputMessage>
     <Address>
          <Street>590 Madison Avenue</Street>
          <Location>Manhattan</Location>
          <County>New York</County>
          <City>New York City</City>
          <State>NW</State>
     </Address>
     <Address>
          <Street>1 New Orchard Road</Street>
          <Location>Armonk</Location>
          <County>Westchester</County>
          <City>New York City</City>
          <State>NY</State>
     </Address>
</InputMessage>

这个映射的用途是,根据输入的 State 字段是否包含有效的美国州缩写,设置输出元素的值。如图 7 所示,这个示例中的 MEMBER 函数接受对象 State:sequence:In1 的值,将它与硬编码的州缩写集内的值进行比较:

图 7. Map_Member 中的映射规则
Map_Member 中的映射规则
Map_Member 中的映射规则

清单 12 给出输出消息,它指出第一个地址包含无效的州缩写 NW

清单 12. Map_Member 的输出消息
<OutputMessage>
     <Result>Address 1 is invalid - the state abbreviation of NW is incorrect</Result>
     <Result>Address 2 is valid - the state abbreviation of NY is correct</Result>
</OutputMessage>

7. 相当于使用动态索引的循环的 WebSphere TX 映射

清单 13 给出 WebSphere TX 映射示例 Map_DynamicIndex 的输入消息。Field 的第一个实例包含一个整数,它指定应该用 Field 的哪个实例填充输出消息。在这个示例中,5 表示应该把值 some_data4 放在输出中:

清单 13. Map_DynamicIndex 的输入消息
<InputMessage>
     <Field>5</Field>
     <Field>some_data1</Field>
     <Field>some_data2</Field>
     <Field>some_data3</Field>
     <Field>some_data4</Field>
     <Field>some_data5</Field>
</InputMessage>

许多编程语言支持字段索引的概念,字段索引用于访问重复字段的特定实例。可以静态地(硬编码为数字)或动态地(设置为变量的值)指定索引。清单 14 所示的伪代码演示这个概念:

清单 14. 演示静态和动态索引值的伪代码
     /* Statically assigning a value (hard coded index) */
     SET RequiredIndexValue = Field[1];
     /* Dynamically assigning a value (index set to the value of a variable) */
     SET OutputField = Field[RequiredIndexValue];

根据 WebSphere TX 映射规则语法,只需把一个数字放在方括号中,即可实现静态赋值,比如表达式 Field[1]:sequence:In1。也可以执行动态赋值,但是不使用方括号语法,而是使用 CHOOSE 函数,如下所示:

图 8. Map_DynamicIndex 中的映射规则
Map_DynamicIndex 中的映射规则
Map_DynamicIndex 中的映射规则

Map_DynamicIndex 生成的输出消息如下所示:

清单 15. Map_DynamicIndex 的输出消息
<OutputMessage>
     <OutputField>some_data4</OutputField>
</OutputMessage>

8. 相当于使用动态字段名的循环的 WebSphere TX 映射

对于这个示例讨论的场景,在原始编程环境或转换工具中,常常通过 XML 结构动态字段名的循环 来解决。请考虑下面的输入消息:

清单 16. Map_DynamicFieldNames 的输入消息
<InputMessage>
     <Field1ToBeIgnored>some_data</Field1ToBeIgnored>
     <FieldToBeTransformed>data_value1</FieldToBeTransformed>
     <LinkedField>no</LinkedField>
     <LinkedField>yes</LinkedField>
     <LinkedField>no</LinkedField>
     <Field2ToBeIgnored>some_data</Field2ToBeIgnored>
     <FieldToBeTransformed>data_value2</FieldToBeTransformed>
     <LinkedField>no</LinkedField>
     <LinkedField>no</LinkedField>
     <Field3ToBeIgnored>some_data</Field3ToBeIgnored>
     <FieldToBeTransformed>data_value3</FieldToBeTransformed>
     <LinkedField>no</LinkedField>
     <LinkedField>no</LinkedField>
     <LinkedField>no</LinkedField>
     <LinkedField>no</LinkedField>
     <LinkedField>no</LinkedField>
     <Field4ToBeIgnored>some_data</Field4ToBeIgnored>
</InputMessage>

这个映射的目的是,寻找名为 FieldToBeTransformed 的所有元素,对于找到的每个元素,在输出消息中添加一个元素,它的值取决于直接跟在 FieldToBeTransformed 后面的所有 LinkedField 元素的值。消息中的所有元素都出现在层次结构的同一层上,都是根标记的直接子元素。至于说到转换规则,插入进来的其他元素必须被导航不能忽略。输入消息的 XML 模式(见清单 17)规定 FieldToBeTransformedLinkedField 元素出现的次数没有上限,都可以选择。

清单 17. Map_DynamicFieldNames 所用的 InputSchema.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xs:element name="InputMessage" type="InputMessageType"/>		
     <xs:complexType name="InputMessageType">
          <xs:choice minOccurs="1" maxOccurs="unbounded">
               <xs:element name="FieldToBeTransformed" type="xs:string" 
                    minOccurs="1" maxOccurs="unbounded"/>
               <xs:element name="LinkedField" type="xs:string" 
                    minOccurs="1" maxOccurs="unbounded"/>
               <xs:element name="Field1ToBeIgnored" type="xs:string"/>
               <xs:element name="Field2ToBeIgnored" type="xs:string"/>
               <xs:element name="Field3ToBeIgnored" type="xs:string"/>
               <xs:element name="Field4ToBeIgnored" type="xs:string"/>
          </xs:choice>
     </xs:complexType>	
</xs:schema>

由于这个定义相当宽泛,满足需求最明显的方法是,用一个循环依次检查每个元素的名称。清单 18 包含的伪代码演示一种典型的编程技术:

清单 18. 演示使用动态字段名的循环的伪代码
   DECLARE Pointer TO FIRSTCHILD OF InputMessage;
   WHILE LASTMOVE(Pointer) DO
    IF FIELDNAME(Pointer) = "FieldToBeTransformed" THEN
      SET StoreOutputValue = FIELDVALUE(Pointer);
      MOVE Pointer NEXTSIBLING;
      WHILE FIELDNAME(Pointer) = "LinkedField" DO
        IF Pointer = "yes" THEN
          SET StoreOutputValue = "At least one LinkedField had value yes!";
        END IF;
          MOVE Pointer NEXTSIBLING;
      END WHILE;
      SET OutputMessage.OutputField = StoreOutputValue;
    END IF;
    MOVE Pointer NEXTSIBLING;	 
  END WHILE;

WebSphere TX 解决这个问题的方法是,使用一个对于每个 FieldToBeTransformed 实例都调用的功能性映射调用。对于功能性映射的每次迭代,使用称为使用对象名协同运算集 的 WebSphere TX 行为传递相关联的 LinkedField 实例集。后面的 13. 在计算映射规则时如何使用对象名协同嵌套的字段 一节将描述这种行为。包含这个功能性映射调用的映射规则如下所示:

图 9. Map_DynamicFieldNames 中的映射规则
Map_DynamicFieldNames 中的映射规则
Map_DynamicFieldNames 中的映射规则

这个功能性映射本身使用 ALL 函数判断 LinkedField 实例是否设置为 yes 值,见图 10。最终的输出消息见清单 19。

图 10. Map_DynamicFieldNames 中的功能性映射 Func_OutputFieldMap 中的映射规则
Map_DynamicFieldNames 中的功能性映射 Func_OutputFieldMap 中的映射规则
Map_DynamicFieldNames 中的功能性映射 Func_OutputFieldMap 中的映射规则
清单 19. Map_DynamicFieldNames 的输出消息
<OutputMessage>
     <OutputField>At least one LinkedField had value yes!</OutputField>
     <OutputField>data_value2</OutputField>
     <OutputField>data_value3</OutputField>
</OutputMessage>

9. 如何在 WebSphere TX 映射规则中引用当前输出

在开发处理递归数据结构的映射时,常常有必要在功能性映射中根据正在生成的输出的索引做出映射决策。通过使用美元符号 ($),可以在映射规则中引用前面生成的字段,也可以引用输出字段的当前实例。例如,请考虑清单 20 所示的输入消息和清单 21 所示的相关输出消息:

清单 20. Map_ReferCurrentOutput 的输入消息
<InputMessage>
     <Field>some_data</Field>
     <Field>some_data</Field>
     <Field>some_data</Field>
     <Field>some_data</Field>
     <Field>some_data</Field>
</InputMessage>
清单 21. Map_ReferCurrentOutput 的输出消息
<OutputMessage>
     <OutputField>some_data1</OutputField>
     <OutputField>some_data2</OutputField>
     <OutputField>some_data3</OutputField>
     <OutputField>some_data4</OutputField>
     <OutputField>some_data5</OutputField>
</OutputMessage>

用来生成每个 OutputField 实例的 WebSphere TX 映射规则传递来自输入消息的当前 Field 实例的值以及当前 OutputField 的索引号(由 INDEX($) 表示),见图 11。功能性映射本身把索引转换为字符格式,然后把这两个值连接起来形成输出值:

图 11. Map_ReferCurrentOutput 中的映射规则
Map_ReferCurrentOutput 中的映射规则
Map_ReferCurrentOutput 中的映射规则

您已经在前面看到了如何使用 EXTRACT 和 CHOOSE 函数在序列中寻找数据对象,这个示例使用另一个搜索函数 SEARCHUP,可以使用它执行二分法搜索。SEARCHUP 函数需要三个参数:

  • 表示函数应该返回哪个相关对象的表达式
  • 要搜索的序列(包含按 ASCII 升序排序的数据)
  • 表示要搜索的值的项

清单 22 给出示例映射的输入消息,它包含数量不限的 Person 结构。每个 Person 包含一个 Name 元素和一个 State 元素。State 值用于搜索查找文件,这个文件包含所有 50 个州及其首府。姓名为 Bill ClintonPerson 元素的 State 值以粗体显示。在清单 23 中,与这个值相关联的查找信息也以粗体显示:

清单 22. Map_Search 的输入消息
<InputMessage>
     <Person>
          <Name>Barack Obama</Name>
          <State>IL</State>
     </Person>
     <Person>
          <Name>George Bush</Name>
          <State>TX</State>
     </Person>	
     <Person>
          <Name>Bill Clinton</Name>
          <State>AR</State>
     </Person>
</InputMessage>

清单 23 显示引用消息的片段,其中包含州及其首府的列表:

清单 23. Map_Search 的引用消息片段
<States>
     <State>
          <Abbreviation>AK</Abbreviation>
          <Name>Alaska</Name>
          <Capital>Juneau</Capital>
     </State>
     <State>
          <Abbreviation>AL</Abbreviation>
          <Name>Alabama</Name>
          <Capital>Montgomery</Capital>
     </State>	
     <State>
          <Abbreviation>AR</Abbreviation>
          <Name>Arkansas</Name>
          <Capital>Little Rock</Capital>
     </State>	

     ... 47 more states removed from this extract! ...

</States>

对于输入消息中的每个 Person 实例,执行图 12 所示的映射规则:

图 12. Map_Search 中的映射规则
Map_Search 中的映射规则
Map_Search 中的映射规则

最终的输出消息见清单 24:

清单 24. Map_Search 的输出消息
<OutputMessage>
     <President>
          <Name>Barack Obama</Name>
          <StateCapital>Springfield</StateCapital>
     </President>
     <President>
          <Name>George Bush</Name>
          <StateCapital>Austin</StateCapital>
     </President>
     <President>
          <Name>Bill Clinton</Name>
          <StateCapital>Little Rock</StateCapital>
     </President>
</OutputMessage>

11. 如何使用 UNIQUE 函数对数据重新分组

可以使用 UNIQUE 函数处理在序列数据对象中找到的所有独特成员。这个示例完成一种常见的任务,在真实环境中用它对数据重新分组。清单 25 所示的输入消息包含数量不限的 Player 元素。每个 Player 有相关联的 NameTeam

清单 25. Map_Unique 的输入消息
<InputMessage>
     <Player>
          <Name>Peter Crouch</Name>
          <Team>Tottenham Hotspur</Team>
     </Player>
     <Player>
          <Name>Jermain Defoe</Name>
          <Team>Tottenham Hotspur</Team>
     </Player>
     <Player>
          <Name>Wayne Rooney</Name>
          <Team>Manchester United</Team>
     </Player>
     <Player>
          <Name>Gareth Bale</Name>
          <Team>Tottenham Hotspur</Team>
     </Player>
     <Player>
          <Name>Michael Owen</Name>
          <Team>Manchester United</Team>
     </Player>
</InputMessage>

这个映射的目的是,把同一球队的球员放在输出数据中的同一部分中。清单 26 显示相应的输出消息,它把几个 Player 元素以及相关的 Team 元素放在 XML 层次结构中的 FootballClub 元素中:

清单 26. Map_Unique 的输出消息
<OutputMessage>
     <FootballClub>
          <Team>Tottenham Hotspur</Team>
          <Player>Peter Crouch</Player>
          <Player>Jermain Defoe</Player>
          <Player>Gareth Bale</Player>
     </FootballClub>
     <FootballClub>
          <Team>Manchester United</Team>
          <Player>Wayne Rooney</Player>
          <Player>Michael Owen</Player>
     </FootballClub>
</OutputMessage>

为了实现这种映射,WebSphere TX 映射 Map_Unique 调用功能性映射 Func_FootballClub,把 UNIQUE 函数(用于寻找输入消息中的所有独特球队名称)的输出以及 sequence:In1 结构中包含的所有球员信息传递给它。调用 Func_FootballClub 的映射规则见图 13:

图 13. Map_Unique 中的映射规则
Map_Unique 中的映射规则
Map_Unique 中的映射规则

功能性映射本身使用 EXTRACT 函数寻找与球队对应的所有球员,见图 14 中的映射规则:

图 14. 功能性映射 Func_FootballClub 中的映射规则
功能性映射 Func_FootballClub 中的映射规则
功能性映射 Func_FootballClub 中的映射规则

12. 如何使用 PRESENT 函数和数据结构产生有条件输出

可以在映射规则中使用 PRESENT 函数编写有条件的 WebSphere TX 映射逻辑,这个函数检查在数据结构中是否可以找到某个数据对象。使用清单 27 所示的输入消息演示这个函数,其中包含 XML 元素 ChoiceChoice 可以出现多次,每个 Choice 包含一个 Option1Option2Option3

清单 27. Map_ChoicePresent 的输入消息
<InputMessage>
     <Choice><Option2>MiXeD_cAsE</Option2></Choice>
     <Choice><Option3>MiXeD_cAsE</Option3></Choice>
     <Choice><Option1>MiXeD_cAsE</Option1></Choice>
</InputMessage>

每个输入元素在生成的输出消息中都有一个对应的元素,见清单 28。这些输出元素的实际内容取决于在输入消息中出现的是哪个元素:

  • 如果是 Option1,那么把数据转换为大写的。
  • 如果是 Option2,那么把数据转换为小写的。
  • 如果是 Option3,那么按原样复制数据。
清单 28. Map_ChoicePresent 的输出消息
<OutputMessage>
     <Result>Option2 present so lowercase data... mixed_case</Result>
     <Result>Option3 present so copy unchanged... MiXeD_cAsE</Result>
     <Result>Option1 present so uppercase data... MIXED_CASE</Result>
</OutputMessage>

实现这个输出的映射规则如下所示:

图 15. Map_ChoicePresent 中的映射规则
Map_ChoicePresent 中的映射规则
Map_ChoicePresent 中的映射规则

13. 在计算映射规则时如何使用对象名协同嵌套的字段

这个示例演示称为对象名协同 的 WebSphere TX 行为。为了帮助解释这个概念,请考虑清单 29 所示的消息示例:

清单 29. Map_ObjectNameCoordination 的输入消息
<InputMessage>
     <Group>
          <Name>Ben Thompson</Name>
          <Name>Joe Bloggs</Name>
          <Userid>bthomps</Userid>
          <Userid>jbloggs</Userid>		
     </Group>
     <Group>
          <Name>John Doe</Name>
          <Userid>jdoe</Userid>
     </Group>
</InputMessage>

在这个示例中,第一个 Group 中有两个 Name 实例和两个 Userid 实例,一共有 4 个元素。但是,如果也考虑第二个 Group,就一共有 6 个元素。当输入数据中有多个 Group 元素时,CountNamesAndUseridsCoordinated 的输出映射规则中出现的 COUNT 函数只包含第一个 Group 中的元素,见图 16:

图 16. Map_ObjectNameCoordination 中的映射规则
Map_ObjectNameCoordination 中的映射规则
Map_ObjectNameCoordination 中的映射规则

由于有对象名协同,NameUserid 的运算集就是(第一个)Group。换句话说,当在同一表达式中多次使用同一对象名时,WebSphere TX 假设两个引用应该与相同对象关联。在某些情况下,这种行为可能不是您想要的。在这些情况下,可以使用关键字 IN 解除对象名关联,见图 17 所示的 CountNamesAndUseridsDecoupled 映射规则:

图 17. Map_ObjectNameCoordination 中的映射规则
Map_ObjectNameCoordination 中的映射规则
Map_ObjectNameCoordination 中的映射规则

输出消息正确地显示执行这两种 COUNT 计算的结果(值为 4 或 6),见清单 30:

清单 30. Map_ObjectNameCoordination 的输出消息
<OutputMessage>
     <CountNamesAndUseridsCoordinated>4</CountNamesAndUseridsCoordinated>
     <CountNamesAndUseridsDecoupled>6</CountNamesAndUseridsDecoupled>
</OutputMessage>

14. 如何使用 EXTRACT 和 SUM 以及嵌套的功能性映射解决复杂的映射问题

这个场景使用的输入消息格式故意设计得很复杂,以此说明本文中到目前为止讨论的标准 WebSphere TX 映射开发技术难以解决的问题。这个输入消息是用于在 XML 元素 Quantity 中传输一系列数据值的数据格式。数据值分组为所谓的数据,每个片提供对应于特定处理日期的数据集。数据值本身放在 XML 消息中名为 MessageData 的部分中,数据片的编号放在前面的 TechnicalHeader 中。输入消息见清单 31:

清单 31. Map_ComplexExtractSum 的输入消息
<InputMessage>
     <TechnicalHeader>
          <ProcessingSlices>
               <Slice>
                    <ProcessingDate>20101225</ProcessingDate>
                    <NumberOfValues>1</NumberOfValues>
               </Slice>
               <Slice>
                    <ProcessingDate>20101226</ProcessingDate>
                    <NumberOfValues>3</NumberOfValues>
               </Slice>
               <Slice>
                    <ProcessingDate>20101227</ProcessingDate>
                    <NumberOfValues>2</NumberOfValues>
               </Slice>
          </ProcessingSlices>
     </TechnicalHeader>
     <MessageData>
          <MeterData>
               <SequenceId>1</SequenceId>
               <Quantity>23</Quantity>
          </MeterData>
          <MeterData>
               <SequenceId>1</SequenceId>
               <Quantity>56</Quantity>
          </MeterData>
          <MeterData>
               <SequenceId>2</SequenceId>
               <Quantity>99</Quantity>
          </MeterData>
          <MeterData>
               <SequenceId>3</SequenceId>
               <Quantity>73</Quantity>
          </MeterData>
          <MeterData>
               <SequenceId>1</SequenceId>
               <Quantity>7</Quantity>
          </MeterData>
          <MeterData>
               <SequenceId>2</SequenceId>
               <Quantity>47</Quantity>
          </MeterData>	
     </MessageData>
</InputMessage>

这个 WebSphere TX 映射要解决的转换问题是,选择属于给定的数据片的所有读数 Quantity 值,然后求出这些值的和。输出消息必须包含整个输出的汇总信息,包括输入中有多少个数据片、包含的 Quantity 值的总数及其总和。除了总体汇总信息之外,每个片还必须有一个 SliceSummary,它们包含片的实际 Quantity 值:

清单 32. Map_ComplexExtractSum 的输出消息
<OutputMessage>
     <OutputSummary>
          <TotalSlices>3</TotalSlices>
          <TotalMeterDataReadings>6</TotalMeterDataReadings>
          <TotalMeterDataValues>305</TotalMeterDataValues>
     </OutputSummary>
     <SliceSummary>
          <OutputData>23</OutputData>
          <SliceTotalMeterDataValues>23</SliceTotalMeterDataValues>
     </SliceSummary>
     <SliceSummary>
          <OutputData>56</OutputData>
          <OutputData>99</OutputData>
          <OutputData>73</OutputData>
          <SliceTotalMeterDataValues>228</SliceTotalMeterDataValues>
     </SliceSummary>
     <SliceSummary>
          <OutputData>7</OutputData>
          <OutputData>47</OutputData>
          <SliceTotalMeterDataValues>54</SliceTotalMeterDataValues>
     </SliceSummary>
</OutputMessage>

初看起来,这个问题似乎不太复杂,但主要难题是提取与特定的片相关的 Quantity。输入消息中的每个 Quantity 元素都出现在 XML 层次结构中的同一层上,而且没有寻找特定片的 Quantity 的简便方法(比如可以识别它们的属性)。另外,每个片开头的 SequenceId 值是从 1 开始的数字编号。

这个映射采用的解决方案是使用 EXTRACT 函数选择当前片的 Quantity。这个 EXTRACT 函数需要标识相关 Quantity 元素在序列对象中的索引的参数。请考虑第二个数据片,其中包含两个单独的 Quantity 值:

  • 计算这个元素集的启始索引值的惟一方法是,求出前面的所有片中出现的值的总数。
  • 计算这个元素集的结束索引值的惟一方法是,求出前面的所有片和当前片中出现的值的总数。

只能通过使用两个功能性映射调用确定这些索引值并隔离递归结构的正确部分。映射的层次结构见图 18 所示的 Composition 视图:

图 18. Map_ComplexExtractSum 的 Composition 视图
Map_ComplexExtractSum 的 Composition 视图
Map_ComplexExtractSum 的 Composition 视图

对于每个数据片,都执行第一个功能性映射调用,见图 19。调用中的第二个参数是当前片的索引:

图 19. Map_ComplexExtractSum 中的映射规则
Map_ComplexExtractSum 中的映射规则
Map_ComplexExtractSum 中的映射规则

第二个功能性映射调用见图 20。调用中的第 5 个参数计算前面所有片和当前片中存在的值的总数:

图 20. Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary 中的映射规则
Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary 中的映射规则
Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary 中的映射规则

最后,EXTRACT 函数收集相关的 Quantity 值,见图 21:

图 21. Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary1 中的映射规则
Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary1 中的映射规则
Map_ComplexExtractSum 中的功能性映射 Func_SliceSummary1 中的映射规则

请注意 EXTRACT 的第二个参数,它定义所需索引值的上限和下限。作为 InMeterDataIndex 引用的卡是通过图 20 中调用的第 5 个参数传递的值,代表索引上限。嵌套的第二个 SUM 和 EXTRACT 函数组合计算下限,下限基于前面的片中的值的总数,在映射规则中表示为 InSliceIndex - 1

这个示例相当复杂,结合使用了多种处理递归数据的技术。为了充分理解这个示例,应该考虑导入本文附带的 WebSphere TX 映射示例代码,仔细研究这个映射。

15. 如何使用文本函数和前面的技术生成自制的基本 XML 解析器

这个示例的目的是,演示如何结合使用本文介绍的技术创建可以解析任何良构 XML 文档的基本映射。WebSphere TX 提供 "Classic" 和 "Xerces" 解析器,它们是处理 XML 数据结构的最好的工具。这里描述的映射不应该作为这两个解析器的替代品 —— 它的性能不如内置的解析器,而且只能处理基本的 XML 元素和属性,不支持 XML 注释和处理指令等比较复杂的结构。这个映射只用来展示有意思的 WebSphere TX 函数。请考虑清单 33 中的简单 XML 消息,它作为这个映射的输入:

清单 33. Map_DIYParserXML 的输入消息
<InputMessage>
     <Field1>some_data1</Field1>
     <Field2>some_data2</Field2>
     <Field3>some_mixed_content_data
          <Field4>
               <Field5>some_data5</Field5>
          </Field4>
     </Field3>
</InputMessage>

输入消息包含简单的 XML 元素(Field1、Field2 和 Field5)、包含混合内容的 XML 元素(Field3)和嵌套的元素(Field4 和 Field5)。解析器必须循环遍历消息中的每个 XML 元素,判断标记的名称(包围在 < 和 > 字符之间)和数据值。数据值是元素的开始标记到元素的结束标记 下一个 XML 元素的开始标记(以先出现的为准)之间的内容。使用对一个功能性映射的调用产生迭代,对于每个元素都必须执行这个调用。使用 COUNTSTRING 函数决定调用的次数,这个函数计算 < 字符出现的次数。其中一些 < 字符是结束标记,调用功能性映射之后在 Func_Line 中使用 IF 语句丢弃它们。图 22 中的映射规则使用 COUNTSTRING 函数的结果和 CLONE 把输入消息多次传递给功能性映射。对功能性映射的每个调用还必须使用 INDEX 函数传递当前的索引号,需要使用索引号从输入消息中提取正确的数据:

图 22. Map_DIYParserXML 的映射规则
Map_DIYParserXML 的映射规则
Map_DIYParserXML 的映射规则

如图 22 所示,对输入数据应用 NORMXML 以删除 XML 空白(包括回车、换行、制表符和空格)。在混合内容包含空白的情况下,这种技术可以改进输出消息的格式。WebSphere TX 映射可以用这个函数轻松地去除 XML 输入中不想要的空白。经过规范化的 XML 输入见清单 34。映射使用 WORD 函数分解这段文本,它使用 < 字符作为分隔符解析文本。第一个字符是分隔符,所以 WORD 找到的第一项是一个空字符串。WORD 找到的第二项是文本 InputMessage>。WORD 找到的第三项是文本 Field1>some_data1,以此类推:

清单 34. 经过规范化的 Map_DIYParserXML 输入消息
<InputMessage><Field1>some_data1</Field1><Field2>some_data2</Field2><Field3>some_mixed_con
tent_data<Field4><Field5>some_data5</Field5></Field4></Field3></InputMessage>

图 23 显示功能性映射 Func_Line1 的输出规则。这个映射使用嵌套的 WORD 函数提取每个 XML 元素的标记和值:

图 23. Map_DIYParserXML 的映射规则
Map_DIYParserXML 的映射规则
Map_DIYParserXML 的映射规则

详细考虑一下 Field2 的情况。功能性映射 Func_Line1 作为输入卡 In1 接收清单 34 所示的数据。在计算标记的输出时,映射规则中的 WORD(In1,"<",In2+1) 部分选择文本 Field2>some_data2。这段文本在清单 34 中以粗体显示。参数 In2+1(求出 4 + 1 = 5)选择第 5 个文本项,它在第 4 个 < 分隔符后面。包围这个部分的 WORD 函数选择文本中的 Field2。在图 23 中,用于确定标记的第三个 WORD 函数删除数据中可能包含的所有 XML 属性。最后,FILLRIGHT 函数用空格填充输出,确保输出的长度总是 25 个字符,这会提高输出消息的可读性。最终的输出消息见清单 35:

清单 35. Map_DIYParserXML 的输出消息
Tag: InputMessage                  Value: <NODATA>
Tag: Field1                        Value: some_data1
Tag: Field2                        Value: some_data2
Tag: Field3                        Value: some_mixed_content_data
Tag: Field4                        Value: <NODATA>
Tag: Field5                        Value: some_data5

相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=680389
ArticleTitle=WebSphere Transformation Extender 的 15 个开发技巧和诀窍
publish-date=06132011