如何 iterate/while 在 IBM Integration Bus(工具包)中将变量从环境映射到消息组件?

How to iterate/while a mapping variables from environment to message assembly in IBM Integration Bus (toolkit)?

我有一个 SOAP 节点,它从树结构中的 URL 检索信息。

然后我有一个计算节点来为 SOAP 检索的每个名称空间变量定义每个环境变量。

最后,我有一个映射节点,用于将内容移动到 XML 中的消息组装结构。

它给我的错误是这样的(在计算节点中):

我有这样的结构:

ListDocs

Description
DocType
ListTypes
 Attribute
 Lenght
 Description
 Nature
 Required

ListDocs

Description
DocType
ListTypes
 Attribute
 Lenght
 Description
 Nature
 Required

ListDocs

Description
DocType
ListTypes
 Attribute
 Lenght
 Description
 Nature
 Required

问题是,当我定义变量时,我在计算节点中像下面的代码那样做:

WHILE I < InputRoot.SOAP.Body.ns:obterTiposDocProcessosResponse.ns:return.ns75:processo.ns75:listaTiposDocumentos  
DO

  SET Environment.Variables.XMLMessage.return.process.listDocs.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:description;

  SET Environment.Variables.XMLMessage.return.process.listDocs.tipoDocumento = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:DocType;

  SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.attribute = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:listTypes.ns75:atribbute;

  SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.lenght = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:listTypes.ns75:lenght;

  SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:listTypes.ns75:description;

  SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.nature = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:listTypes.ns75:nature;

  SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.required = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs.ns75:listTypes.ns75:required;    

  SET I = I+1;

END WHILE;

但是,在我的 XML 最终结构中,它只打印我的第一个 listDocs 的值,而我想打印我所有的 listDocs 结构。

注意:像这样,它甚至不起作用。我必须像上面说的那样删除打印第一个 listDocs 的时间。

有什么帮助吗?

我需要帮助来循环结构,用一段时间或其他东西。

您应该尝试使用以下语法:

DECLARE I INTEGER 1;
DECLARE J INTEGER;
J = CARDINALITY(InputRoot.SOAP.Body.ns:obterTiposDocProcessosResponse.ns:return.ns75:processo.ns75:listaTiposDocumentos[])
WHILE I <= J DO
   SET Environment.Variables.XMLMessage.return.process.listDocs.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:description;
   ....
END WHILE;

你只是错过了获取元素数量的 CARDINALITY 函数,以及定义 table 的 [],然后在访问元素时使用此 [I]

注意:在我上面的示例中,环境将在循环的每次迭代中被覆盖,因此只会打印最后一条记录。如果你想在输出中构造一个 table ,你也可以在输出中使用 [I] ,或者你可以使用以下代码将每条消息推送到输出终端(这意味着你在输入中有一条消息, 和 3 条消息从输出终端出来)

PROPAGATE TO TERMINAL 'Out';

因此,例如,根据您的代码,如果您想根据包含多个元素的输入生成 3 条消息:

DECLARE I INTEGER 1;
DECLARE J INTEGER;
J = CARDINALITY(InputRoot.SOAP.Body.ns:obterTiposDocProcessosResponse.ns:return.ns75:processo.ns75:listaTiposDocumentos[])
WHILE I <= J DO
   SET Environment.Variables.XMLMessage.return.process.listDocs.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:description;
   SET Environment.Variables.XMLMessage.return.process.listDocs.tipoDocumento = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:DocType;
   SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.attribute = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:atribbute;
   SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.lenght = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:lenght;
   SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:description;
   SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.nature = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:nature;
   SET Environment.Variables.XMLMessage.return.process.listDocs.listTypes.required = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:required;
   PROPAGATE TO TERMINAL 'Out';
END WHILE;
RETURN FALSE;

对于您的全局信息,RETURN TRUE 是指令 "pushing" ESQL 代码中内置到输出终端的消息。如果您使用 PROPAGATE 指令(效果相同),您应该 RETURN FALSE 以避免在记录循环后发送空消息。另一种方法是在另一个终端上传播(即:'out1'),并保持 return 为真。在这种情况下,一旦所有消息都已传播(这可能对许多情况)

因此,理解 IIB 和 ESQL 的关键是您在内存中查看从节点构建的树。

每个节点有 pointers/REFERENCEs 到 PARENT、NEXTSIBLING、PREVSIBLING、FIRSTCHILD 和 LASTCHILD 节点。

节点也有 FIELDNAME、FIELDNAMESPACE、FIELDTYPE 和 FIELDVALUE 属性。

最后但同样重要的是,您正在通过导航输入树来构建输出树。您正在使用的环境树是一种特殊的持久树,您可以对其进行读取和写入。

所以在你的代码中 InputRoot.SOAP.Body.ns75:processo.ns75:listDocs 可以被认为是 shorthand 用于导航到 ns75:listDocs 节点。点'.'告诉 ESQL 解释器当前节点的 child 节点的名称。如果你告诉别人如何导航节点,它会像这样。

  1. InputRoot 开始。 InputRoot 是一个特殊的节点,在您的 ESQL 模块代码中自动提供给您。
  2. 导航到名称为 SOAP
  3. InputRoot 的第一个 child 节点
  4. 导航到名称为 Body
  5. SOAP 的第一个 child 节点
  6. 导航到 Body 的第一个 child 节点,该节点的名称为 listDocs 并且位于 ns75 命名空间。

在没有下标的情况下,ESQL 假定您需要第一个与指定名称匹配的节点 ns75:listDocsns75:listDocs[1] 都引用同一个节点。

这解释了您的代码中发生的事情。您总是导航到 InputRootEnvironment 树中的相同 listDocs[1] 节点。

@Jerem 的代码通过至少在输入树中的 listDocs 节点中导航来改进您正在做的事情。

对于循环的每次迭代,下标 [I] 都会递增,因此它会选择不同的 listDocs 节点。 listDocs 节点是兄弟节点,因此代码将访问 listDocs 节点的第一个、第二个和第三个实例。

InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[1] <-- Iteration I=1
InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[2] <-- Iteration I=2
InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[3] <-- Iteration I=3

要更正@Jerem 的回答,您还需要在语句的左侧使用下标。以 description 字段为例,您需要按如下方式更改代码。

SET Environment.Variables.XMLMessage.return.process.listDocs[I].listTypes.description = InputRoot.SOAP.Body.ns75:processo.ns75:listDocs[I].ns75:listTypes.ns75:description;

使用下标被认为是性能不行。假设您有 10,000 个 listDocs,这将导致循环的每次迭代都沿着树遍历 InputRootSOAP , Body, ns75:processo 节点然后跨越 listDocs 兄弟节点直到它找到了 ns75:listDocs[I] 节点。

这意味着当我们开始处理 ns75:listDocs[10000] 时,它将不得不重复遍历所有其他 listDocs nodes notes time and time, 实际上我们可以计算它会走过 (4 x 10,000) + ((10,000 x (10,000 + 1)) / 2) = 50,045,000 Nodes

所以它是救援的参考,也是您问题的答案。试试这样的循环。

DECLARE ns75 NAMESPACE 'http://something.or.other.from.your.wsdl';
DECLARE InListDocsRef REFERENCE TO 
InputRoot.SOAP.Body.ns75:processo.ns75:listDocs;
WHILE LASTMOVE(InListDocsRef) DO
    DECLARE EnvListDocsRef REFERENCE TO Environment;
    CREATE LASTCHILD OF Environment.Variables.XMLMessage.return.process AS EnvListDocsRef NAME 'listDocs';

    SET EnvListDocsRef.description           = InListDocsRef.ns75:description;
    SET EnvListDocsRef.tipoDocumento         = InListDocsRef.ns75:DocType;
    SET EnvListDocsRef.listTypes.attribute   = InListDocsRef.ns75:listTypes.ns75:atribbute;
    SET EnvListDocsRef.listTypes.lenght      = InListDocsRef.ns75:listTypes.ns75:lenght;
    SET EnvListDocsRef.listTypes.description = InListDocsRef.ns75:listTypes.ns75:description;
    SET EnvListDocsRef.listTypes.nature      = InListDocsRef.ns75:listTypes.ns75:nature;
    SET EnvListDocsRef.listTypes.required    = InListDocsRef.ns75:listTypes.ns75:required;    

    MOVE InListDocsRef NEXTSIBLING REPEAT NAME;
END WHILE;

上面的代码只遍历了 4 + 10,000 个节点,即 1 万个节点与 5000 万个节点。

关于设置引用的其他一些有用的知识是:

  1. 要指向最后一个元素,您可以使用 [<] 的下标。因此,要指向聚合 MyList 中的最后一个 ListItem,您将编码 Environment.MyList.ListItem[<]
  2. 您可以使用星号 * 来设置对树中您不知道其名称的元素的引用,例如Environment.MyAggregate.* 指向 MyAggregate 的第一个 child 而不管它的名称。
  3. 您还可以使用星号 * 来选择一个元素,而不管它的命名空间如何 InListDocsRef.*:listTypes.*:description
  4. 对于匿名命名空间元素使用 *:* 但要非常小心 **:* 不是一回事,第一个表示没有命名空间任何元素,第二个表示任何命名空间任何元素。
  5. 要反向处理列表,请将 [<] 下标与 MOVE[=103] 的 PREVIOUSSIBLING 选项结合起来=].

因此,一段用于反转列表的代码可能如下所示:

DECLARE MyReverseListItemWalkingRef REFERENCE TO Environment.MyList.ListItem[<];
WHILE LASTMOVE(MyReverseListItemWalkingRef) DO
    CREATE LASTCHILD OF OuputRoot.ReversedList.Item NAME 'Description' VALUE MyReverseListItemWalkingRef.Desc;

    MOVE MyReverseListItemWalkingRef PREVIOUSSIBLING REPEAT NAME;
END WHILE;

了解如何使用 REFERENCES,它们非常强大,是您在性能方面最简单的选择之一。