如何通过使用 BizTalk 映射器将 2 个不同的重复节点加入 1 个重复节点来进行映射
How to map by joining 2 different repeating nodes into a 1 repeating node using BizTalk mapper
问题是我需要遍历一个重复节点,但还要从基于 LineId 匹配的兄弟重复节点中提取信息。如果这是一个数据库,我会将每个重复节点等同于一个 table,并希望根据匹配的 ID 输出两个 table 的连接。
我从 Root/Shipments/Shipment/Containers/Container 循环到 Document/Header/Detail 开始,这似乎可行,因为我想为来自源的每个容器创建 1 条记录作为目标中的 1 条详细记录。但是当我映射 Root/Shipments/Shipment/Details/Detail 中的字段时,我只从映射的第一条记录中获取数据,而不是具有匹配 LineId 的记录。然后我尝试将 Root/Shipments/Shipment/Details/Detail 添加到循环中并有条件地抑制 Document/Detail 其中 LineId 在节点之间不匹配但不起作用。因此,我尝试在具有相同抑制条件的 Detail 上添加第二个循环,但也没有用。然后我想我会尝试使用 XSLT 模板 "lookup" 基于 LineId 作为输入的 Root/Shipments/Shipment/Details/Detail 中的数据,但它从未返回任何数据(它始终为空)。此外,要在字段级别分别获取每个字段,我需要搜索 X 次,我需要映射的每个字段搜索 1 次,而不是每条记录搜索 1 次,因此这似乎也很低效。
我的 XSLT 知识有限。如果可能的话,我宁愿使用带有脚本 functoid 的映射而不是纯 XSLT。性能也将是一个问题,因为在这个项目中我需要做的文件数量非常多。我在下面给出的示例是我需要对更多字段执行的操作的非常精简版本,并且源文件的层次结构有更多层。
实现我需要做的事情的最佳方法是什么?
源文件:
<Root>
<Shipments>
<Shipment>
<ShipId>1</ShipId>
<Details>
<Detail>
<LineId>1</LineId>
<RequestedQty>10</RequestedQty>
<Sku>347</Sku>
<Status>C</Status>
</Detail>
<Detail>
<LineId>2</LineId>
<RequestedQty>5</RequestedQty>
<Sku>125</Sku>
<Status>P</Status>
</Detail>
<Detail>
<LineId>3</LineId>
<RequestedQty>8</RequestedQty>
<Sku>337</Sku>
<Status>O</Status>
</Detail>
<Detail>
<LineId>4</LineId>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
</Detail>
</Details>
<Containers>
<Container>
<ContainerId>1</ContainerId>
<Details>
<Detail>
<LineId>1</LineId>
<Lot>103</Lot>
<ShipQty>10</ShipQty>
</Detail>
<Detail>
<LineId>2</LineId>
<Lot>102</Lot>
<ShipQty>3</ShipQty>
</Detail>
</Details>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Container>
<Container>
<ContainerId>2</ContainerId>
<Details>
<Detail>
<LineId>4</LineId>
<Lot>101</Lot>
<ShipQty>10</ShipQty>
</Detail>
<Detail>
<LineId>4</LineId>
<Lot>105</Lot>
<ShipQty>2</ShipQty>
</Detail>
</Detail>
</Details>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Container>
</Containers>
</Shipment>
</Shipments>
</Root>
进入:
<Document>
<Header>
<ShipId>1</ShipId>
<Detail>
<ContainerId>1</ContainerId>
<LineId>1</LineId>
<Lot>103</Lot>
<ShipQty>10</ShipQty>
<RequestedQty>10</RequestedQty>
<Sku>347</Sku>
<Status>C</Status>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>1</ContainerId>
<LineId>2</LineId>
<Lot>102</Lot>
<ShipQty>3</ShipQty>
<RequestedQty>5</RequestedQty>
<Sku>125</Sku>
<Status>P</Status>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>2</ContainerId>
<LineId>4</LineId>
<Lot>101</Lot>
<ShipQty>10</ShipQty>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>2</ContainerId>
<LineId>4</LineId>
<Lot>105</Lot>
<ShipQty>2</ShipQty>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Detail>
</Header>
</Document>
我不确定这是最好的解决方案,但我有一些可以帮助您的东西。这个想法是使用脚本 functoid 在目标文档中创建详细信息元素。
考虑到你有很多元素,我建议按照以下方法来正确命名:
创建一个直接 link,其中包含您希望在目标详细信息元素中显示的所有元素。
"Validate Map" 获取 xslt。复制细节元素及其内容。
删除所有直接 link 到 Detail 元素。
在源 Container/Details/Detail 和目标详细信息之间添加循环 functoid。
添加脚本 functoid,将其连接到目标详细信息。 Select 内联 XSLT 并粘贴您在第 2 步中复制的代码。
测试地图,验证它的大部分工作(尽管 Shipment/Details/Detail 仍然是错误的)
修改脚本 functoid 中的 xslt。您想要一个变量指向与当前行相对应的 Shipment/Details/Detail 元素。
根据当前LineId设置变量:
<xsl:variable name="CurrentShipmentDetail" select="../../../../Details/Detail[LineId=current()/LineId]"/>
- 修改元素,使您 select 从变量
之前:
<RequestedQty>
<xsl:value-of select="../../../../Details/Detail/RequestedQty/text()" />
</RequestedQty>
之后:
<RequestedQty>
<xsl:value-of select="$CurrentShipmentDetail/RequestedQty/text()" />
</RequestedQty>
完整地图:
以及完整的 xslt 代码:
<Detail>
<ContainerId>
<xsl:value-of select="../../ContainerId/text()" />
</ContainerId>
<LineId>
<xsl:value-of select="LineId/text()" />
</LineId>
<Lot>
<xsl:value-of select="Lot/text()" />
</Lot>
<ShipQty>
<xsl:value-of select="ShipQty/text()" />
</ShipQty>
<xsl:variable name="CurrentShipmentDetail" select="../../../../Details/Detail[LineId=current()/LineId]"/>
<RequestedQty>
<xsl:value-of select="$CurrentShipmentDetail/RequestedQty/text()" />
</RequestedQty>
<Sku>
<xsl:value-of select="$CurrentShipmentDetail/Sku/text()" />
</Sku>
<Status>
<xsl:value-of select="$CurrentShipmentDetail/Status/text()" />
</Status>
<TrackingNUmber>
<xsl:value-of select="../../TrackingNUmber/text()" />
</TrackingNUmber>
</Detail>
您应该可以使用 Looping 和 Equal Functoid 来做到这一点。
将 Shipment/Detail 连接到 Looping Functoid,然后将 Container/Detail 连接到同一个 Looping Functoid。
然后将 Shipment//LineId 和 Container//LindId 连接到 Equal Functoid,然后将 Equal Functoid 连接到目标详细信息。
您将看到 Mapper 如何在 xsl 中构建循环和条件。您可以从那里进行调整。
问题是我需要遍历一个重复节点,但还要从基于 LineId 匹配的兄弟重复节点中提取信息。如果这是一个数据库,我会将每个重复节点等同于一个 table,并希望根据匹配的 ID 输出两个 table 的连接。
我从 Root/Shipments/Shipment/Containers/Container 循环到 Document/Header/Detail 开始,这似乎可行,因为我想为来自源的每个容器创建 1 条记录作为目标中的 1 条详细记录。但是当我映射 Root/Shipments/Shipment/Details/Detail 中的字段时,我只从映射的第一条记录中获取数据,而不是具有匹配 LineId 的记录。然后我尝试将 Root/Shipments/Shipment/Details/Detail 添加到循环中并有条件地抑制 Document/Detail 其中 LineId 在节点之间不匹配但不起作用。因此,我尝试在具有相同抑制条件的 Detail 上添加第二个循环,但也没有用。然后我想我会尝试使用 XSLT 模板 "lookup" 基于 LineId 作为输入的 Root/Shipments/Shipment/Details/Detail 中的数据,但它从未返回任何数据(它始终为空)。此外,要在字段级别分别获取每个字段,我需要搜索 X 次,我需要映射的每个字段搜索 1 次,而不是每条记录搜索 1 次,因此这似乎也很低效。
我的 XSLT 知识有限。如果可能的话,我宁愿使用带有脚本 functoid 的映射而不是纯 XSLT。性能也将是一个问题,因为在这个项目中我需要做的文件数量非常多。我在下面给出的示例是我需要对更多字段执行的操作的非常精简版本,并且源文件的层次结构有更多层。
实现我需要做的事情的最佳方法是什么?
源文件:
<Root>
<Shipments>
<Shipment>
<ShipId>1</ShipId>
<Details>
<Detail>
<LineId>1</LineId>
<RequestedQty>10</RequestedQty>
<Sku>347</Sku>
<Status>C</Status>
</Detail>
<Detail>
<LineId>2</LineId>
<RequestedQty>5</RequestedQty>
<Sku>125</Sku>
<Status>P</Status>
</Detail>
<Detail>
<LineId>3</LineId>
<RequestedQty>8</RequestedQty>
<Sku>337</Sku>
<Status>O</Status>
</Detail>
<Detail>
<LineId>4</LineId>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
</Detail>
</Details>
<Containers>
<Container>
<ContainerId>1</ContainerId>
<Details>
<Detail>
<LineId>1</LineId>
<Lot>103</Lot>
<ShipQty>10</ShipQty>
</Detail>
<Detail>
<LineId>2</LineId>
<Lot>102</Lot>
<ShipQty>3</ShipQty>
</Detail>
</Details>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Container>
<Container>
<ContainerId>2</ContainerId>
<Details>
<Detail>
<LineId>4</LineId>
<Lot>101</Lot>
<ShipQty>10</ShipQty>
</Detail>
<Detail>
<LineId>4</LineId>
<Lot>105</Lot>
<ShipQty>2</ShipQty>
</Detail>
</Detail>
</Details>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Container>
</Containers>
</Shipment>
</Shipments>
</Root>
进入:
<Document>
<Header>
<ShipId>1</ShipId>
<Detail>
<ContainerId>1</ContainerId>
<LineId>1</LineId>
<Lot>103</Lot>
<ShipQty>10</ShipQty>
<RequestedQty>10</RequestedQty>
<Sku>347</Sku>
<Status>C</Status>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>1</ContainerId>
<LineId>2</LineId>
<Lot>102</Lot>
<ShipQty>3</ShipQty>
<RequestedQty>5</RequestedQty>
<Sku>125</Sku>
<Status>P</Status>
<TrackingNUmber>Z934793498923984</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>2</ContainerId>
<LineId>4</LineId>
<Lot>101</Lot>
<ShipQty>10</ShipQty>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Detail>
<Detail>
<ContainerId>2</ContainerId>
<LineId>4</LineId>
<Lot>105</Lot>
<ShipQty>2</ShipQty>
<RequestedQty>12</RequestedQty>
<Sku>7438</Sku>
<Status>C</Status>
<TrackingNUmber>Z531365161663161</TrackingNUmber>
</Detail>
</Header>
</Document>
我不确定这是最好的解决方案,但我有一些可以帮助您的东西。这个想法是使用脚本 functoid 在目标文档中创建详细信息元素。
考虑到你有很多元素,我建议按照以下方法来正确命名:
创建一个直接 link,其中包含您希望在目标详细信息元素中显示的所有元素。
"Validate Map" 获取 xslt。复制细节元素及其内容。
删除所有直接 link 到 Detail 元素。
在源 Container/Details/Detail 和目标详细信息之间添加循环 functoid。
添加脚本 functoid,将其连接到目标详细信息。 Select 内联 XSLT 并粘贴您在第 2 步中复制的代码。
测试地图,验证它的大部分工作(尽管 Shipment/Details/Detail 仍然是错误的)
修改脚本 functoid 中的 xslt。您想要一个变量指向与当前行相对应的 Shipment/Details/Detail 元素。
根据当前LineId设置变量:
<xsl:variable name="CurrentShipmentDetail" select="../../../../Details/Detail[LineId=current()/LineId]"/>
- 修改元素,使您 select 从变量
之前:
<RequestedQty>
<xsl:value-of select="../../../../Details/Detail/RequestedQty/text()" />
</RequestedQty>
之后:
<RequestedQty>
<xsl:value-of select="$CurrentShipmentDetail/RequestedQty/text()" />
</RequestedQty>
完整地图:
以及完整的 xslt 代码:
<Detail>
<ContainerId>
<xsl:value-of select="../../ContainerId/text()" />
</ContainerId>
<LineId>
<xsl:value-of select="LineId/text()" />
</LineId>
<Lot>
<xsl:value-of select="Lot/text()" />
</Lot>
<ShipQty>
<xsl:value-of select="ShipQty/text()" />
</ShipQty>
<xsl:variable name="CurrentShipmentDetail" select="../../../../Details/Detail[LineId=current()/LineId]"/>
<RequestedQty>
<xsl:value-of select="$CurrentShipmentDetail/RequestedQty/text()" />
</RequestedQty>
<Sku>
<xsl:value-of select="$CurrentShipmentDetail/Sku/text()" />
</Sku>
<Status>
<xsl:value-of select="$CurrentShipmentDetail/Status/text()" />
</Status>
<TrackingNUmber>
<xsl:value-of select="../../TrackingNUmber/text()" />
</TrackingNUmber>
</Detail>
您应该可以使用 Looping 和 Equal Functoid 来做到这一点。
将 Shipment/Detail 连接到 Looping Functoid,然后将 Container/Detail 连接到同一个 Looping Functoid。
然后将 Shipment//LineId 和 Container//LindId 连接到 Equal Functoid,然后将 Equal Functoid 连接到目标详细信息。
您将看到 Mapper 如何在 xsl 中构建循环和条件。您可以从那里进行调整。