如何 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 节点的名称。如果你告诉别人如何导航节点,它会像这样。
- 从 InputRoot 开始。 InputRoot 是一个特殊的节点,在您的 ESQL 模块代码中自动提供给您。
- 导航到名称为 SOAP
的 InputRoot 的第一个 child 节点
- 导航到名称为 Body
的 SOAP 的第一个 child 节点
- 导航到 Body 的第一个 child 节点,该节点的名称为 listDocs 并且位于 ns75 命名空间。
在没有下标的情况下,ESQL 假定您需要第一个与指定名称匹配的节点 ns75:listDocs 和 ns75:listDocs[1] 都引用同一个节点。
这解释了您的代码中发生的事情。您总是导航到 InputRoot 和 Environment 树中的相同 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,这将导致循环的每次迭代都沿着树遍历 InputRoot、SOAP , 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 万个节点。
关于设置引用的其他一些有用的知识是:
- 要指向最后一个元素,您可以使用 [<] 的下标。因此,要指向聚合 MyList 中的最后一个 ListItem,您将编码 Environment.MyList.ListItem[<]
- 您可以使用星号 * 来设置对树中您不知道其名称的元素的引用,例如Environment.MyAggregate.* 指向 MyAggregate 的第一个 child 而不管它的名称。
- 您还可以使用星号 * 来选择一个元素,而不管它的命名空间如何 InListDocsRef.*:listTypes.*:description
- 对于匿名命名空间元素使用 *:* 但要非常小心 * 和 *:* 不是一回事,第一个表示没有命名空间任何元素,第二个表示任何命名空间任何元素。
- 要反向处理列表,请将 [<] 下标与 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,它们非常强大,是您在性能方面最简单的选择之一。
我有一个 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 节点的名称。如果你告诉别人如何导航节点,它会像这样。
- 从 InputRoot 开始。 InputRoot 是一个特殊的节点,在您的 ESQL 模块代码中自动提供给您。
- 导航到名称为 SOAP 的 InputRoot 的第一个 child 节点
- 导航到名称为 Body 的 SOAP 的第一个 child 节点
- 导航到 Body 的第一个 child 节点,该节点的名称为 listDocs 并且位于 ns75 命名空间。
在没有下标的情况下,ESQL 假定您需要第一个与指定名称匹配的节点 ns75:listDocs 和 ns75:listDocs[1] 都引用同一个节点。
这解释了您的代码中发生的事情。您总是导航到 InputRoot 和 Environment 树中的相同 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,这将导致循环的每次迭代都沿着树遍历 InputRoot、SOAP , 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 万个节点。
关于设置引用的其他一些有用的知识是:
- 要指向最后一个元素,您可以使用 [<] 的下标。因此,要指向聚合 MyList 中的最后一个 ListItem,您将编码 Environment.MyList.ListItem[<]
- 您可以使用星号 * 来设置对树中您不知道其名称的元素的引用,例如Environment.MyAggregate.* 指向 MyAggregate 的第一个 child 而不管它的名称。
- 您还可以使用星号 * 来选择一个元素,而不管它的命名空间如何 InListDocsRef.*:listTypes.*:description
- 对于匿名命名空间元素使用 *:* 但要非常小心 * 和 *:* 不是一回事,第一个表示没有命名空间任何元素,第二个表示任何命名空间任何元素。
- 要反向处理列表,请将 [<] 下标与 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,它们非常强大,是您在性能方面最简单的选择之一。