65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 BizTalk Server 2013 R2 进行集成学习

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018 年 7 月 5 日

CPOL

7分钟阅读

viewsIcon

7105

在生成管道分隔的平面文件时遇到的问题和发现的学习

引言

最近,我们参与了一个 BizTalk 项目,该项目要求生成管道分隔的平面文件 (FF)。由于此任务的各种要求,我们学到了很多东西。我希望在这篇文章中分享一些经验,这些经验可能有助于他人在开发过程中。

首先,我们将简要介绍作为此任务一部分要实现的需求的不同方面。

  • 订单 XML 文件来自 EOM(企业订单管理)系统,必须将其转换为管道分隔的 FF 格式,并带有特定的映射和规则
  • 平面文件需要为每个发票及其行重复行。例如,如果订单 XML 有 2 个发票,每个发票有 3 行,那么平面文件中有 2 个标头,每个标头下有 2 个行项目
    Input XML File:               Output Flat file:
    <Invoice1>                    HEADER|field1|field2…..
    <Line1>                         LINE|field1|field2..
    <Line2>                         LINE|field1|field2…
    </Invoice1>
    <Invoice2>                    HEADER|field1|field2…..                                            
    <Line1>                         LINE|field1|field2..
    <Line2>                         LINE|field1|field2..
    </Invoice2>
  • 平面文件需要在所有内容之前,将文件放置在客户端位置之前,在顶部有一个标头元素
  • 需要为销售和调整在不同的位置生成单独的平面文件
  • 调整的管道分隔格式需要具有动态内容而不是固定元素。
  • 作为 PCI DSS 合规性的一部分,信用卡号在传输过程中应加密,仅在将文件放置在客户端位置时解密
  • 处理平面文件中的可选元素,需要将销售架构中的所有 FF XSD 元素都设置为 Choice,minOccurs=0maxoccurs 为 unbounded
  • FF 中的标头级别项目需要计算不同行级别值(例如金额、税金等)的总和或平均值
  • FF 反汇编器和汇编器命令行工具已用于测试 FF 架构是否没有任何问题

我们将详细介绍这些功能,并解释如何使用 BizTalk 2013 R2 解决这些问题。

订单 XML 文件来自 EOM(企业订单管理)系统,必须将其转换为管道分隔的 FF 格式,并带有特定的映射和规则

我们使用了一种中间规范 XML 格式来转换输入订单 XML 文件,然后再转换为 FF。业务逻辑和循环在此转换中实现,并存储为规范 XML 字段。在将规范转换为输出 FF 期间,其思想是进行直接的一对一映射,其中没有太多的业务逻辑。规范格式还有助于将一个或多个源或目标系统添加到要集成的系统。在这些情况下,我们希望将源转换为规范和/或将规范转换为目标格式。如果未使用规范架构方法,我们需要维护不同的映射以在不同的源和目标格式之间进行转换,这很麻烦。

根据 BizTalk 实践,规范到平面文件转换需要将 FF 架构指定在发送端口中以生成平面文件。

订单 XML 文件来自 EOM(企业订单管理)系统,必须将其转换为管道分隔的 FF 格式,并带有特定的映射和规则。

FF 需要为每个发票及其行重复行

我们使用 XSLT 进行规范到输出 FF 格式的整个转换过程。因此,在 XSLT 中实现这一点相对简单,因为我们可以使用 for each 循环遍历输入 XML 中的每个发票节点,并为每个输入发票和行重复形成输出元素。

<xsl:for-each select="// Invoices">
<Element1></Element1>
<Element2></Element2>
…
<xsl:for-each select="// Invoices/Line">
<LineElement1></LineElement1>
<LineElement2></LineElement2>
…

FF 需要在所有内容之前,在顶部有一个标头元素

Sample Header:

<Header download_id="SALE_20180322121907.txt" deployment_name="ORDER_SALE" download_time="IMMEDIATE" />

这通过在 BizTalk FF 架构中创建一个可选的 Header 元素来实现。这可以使用 FFAsm.exe 测试 XML 到平面文件的生成,或者使用 FFDAsm.exe 测试 FF 到 XML 的转换。我们将在后面的部分中详细介绍这些工具以及如何使用它们。

需要为销售和调整在不同的位置生成单独的平面文件

我们在规范标头中添加了一个额外的字段 InvoiceType,它将具有值“Sales”或“Adjustment”。输入 XML 到规范 XML 转换将根据输入文件字段值适当地填充此值。此外,InvoiceType 在规范架构中提升,以便可以在发送端口筛选器中查看它。一旦查看,我们就可以在发送端口中检查此条件,并使用它在不同的位置生成文件。

SendPortAdjustment              -              Filter Condition: InvoiceType == Adjustment
SendPortSale                    -              Filter Condition: InvoiceType == Sale

调整的管道分隔格式需要具有动态内容而不是固定元素。

我们要求以以下格式生成调整平面文件

RUN_SQL|INSERT INTO TABLE(FIELD1, FIELD2, ….) VALUES (FIELDVAL1, FIELDVAL2, …)
RUN_SQL|INSERT INTO TABLE(FIELD1, FIELD2, ….) VALUES (FIELDVAL1, FIELDVAL2, …)

为此,我们创建了 FF 架构,其元素具有 tag_name=”RUN_SQL|”。因此,它将构造以 RUN_SQL 作为起始标签,并在管道分隔符后紧接着 INSERT 命令的 FF。我们使用 XSLT 和 C# 构建值为 RUN_SQL|INSERT INTO TABLE ... 并将动态构建的 insert 查询分配给 XSD 元素。构建的架构 XML 可以使用管道工具 FFAsm.exe/FFDAsm.exe 转换为/从 FF。

作为 PCI DSS 合规性的一部分,信用卡号在传输过程中应加密,仅在将文件放置在客户端位置时解密

TenderIdPRIVATE_CC 时,信用卡号需要解密。我们使用如下所示的 XSLT 模板实现了这一点。如果 TenderIdPRIVATE_CC,则下面的 XSLT 代码将源中除了 AccountNumber 之外的所有元素复制到目标。如果是 AccountNumber,它会调用 C# 函数对其进行解密并将其存储在同一元素中。下面的代码应用于映射时,使用 C# 函数 fcnDecryptString 完成信用卡号的解密工作。

<xsl:template name="Copy" match="@* | node()">
  <xsl:param name="param" />
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

<xsl:template match=" CREDITDEBIT_TENDERLINEITEM[TenderId='PRIVATE_CC']">

<xsl:variable name="varEncryptedAcctNo" select="AccountNumber"/>
       <xsl:element name="{name()}">
          <xsl:for-each select="./*">           
            <xsl:choose>
              <xsl:when test="name()='AccountNumber'">
                <xsl:element name="AccountNumber">
                 <xsl:variable name="result" xmlns:ScriptS0="http://SPFunctionHelper" 
                             select="ScriptS0:fcnDecryptString($varEncryptedAcctNo)" />
                  <xsl:value-of select="$result"/>
                </xsl:element>
                </xsl:when>
                <xsl:otherwise>                                            
                   <xsl:copy-of select="."/>
                   </xsl:otherwise>
             </xsl:choose>
          </xsl:for-each>
       </xsl:element>
</xsl:template>

从 XSLT 调用外部程序集中的 C# 函数是使用 此博客 中建议的方法完成的。

处理 FF 中的可选元素,需要将销售架构中的所有 FF XSD 元素都设置为 Choice,minOccurs=0maxoccurs 为 unbounded

我们收到了 FF XML 文件(基于 FF 架构),其中包含元素,或者有时会缺少某些元素。即使缺少某些元素,FF XML 架构也需要成功创建平面文件或将其解析为 XML。

为此,我们需要创建 Root 元素,并在根内部使用复杂类型,其中包含一个 minOccurs 0maxOccurs unbounded 的 choice 元素,如下所示。这将支持 FILE_HEADERFILE_TRANSLINEITEM 存在或缺失的场景,并且在这两种情况下,管道分隔的 FF 都将成功从 XML 生成或解析为 XML 文件。

<xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
…
<xs:element minOccurs="0" maxOccurs="unbounded" name="FILE_HEADER">
<xs:annotation>
            <xs:appinfo>
              <b:recordInfo structure="delimited" 
              child_delimiter_type="char" child_delimiter="|" 
              child_order="prefix" preserve_delimiter_for_empty_data="true" 
              suppress_trailing_delimiters="false" sequence_number="1" 
              tag_name="INSERT|TRANS_HEADER" />
            </xs:appinfo>
          </xs:annotation>
…
<xs:element minOccurs="0" 
maxOccurs="unbounded" name="FILE_TRANSLINEITEM">
          <xs:annotation>
            <xs:appinfo>
              <b:recordInfo structure="delimited" child_delimiter_type="char" 
              child_delimiter="|" child_order="prefix" 
              preserve_delimiter_for_empty_data="true" 
              suppress_trailing_delimiters="false" 
              sequence_number="2" tag_name="INSERT|TRANS_LINE_ITEM" />
            </xs:appinfo>
          </xs:annotation
…

FF 中的标头级别项目需要计算不同行级别值(例如金额、税金等)的总和或平均值

这已通过使用节点集概念完成,并为此提供了 xslt。如果订单有多个行项目,我们有一些要求计算行项目中所有金额的总和,并将它们存储在标头级别的字段中。

<xsl:variable name="tmpTotal">
        <total_amount>
            <xsl:for-each select="Item">
<itemtotal>
<xsl:value-of select="format-number(Item/Amount,'###,###,##0.00')" />
</itemtotal>
<xsl:variable name="varTotal" select="msxsl:node-set($tmpTotal)"/>
      <xsl:variable name="varTotalofAllItems" xmlns:ScriptS0="http://SPFunctionHelper" 
       select="ScriptS0:RoundValue(sum($varTotal/total_amount/itemtotal)" />

上述 XSLT 创建了一个名为 tmpTotal 的变量,该变量维护所有行项目金额的单个值。它将单个金额存储在 itemtotal 变量中,该变量可以使用节点集变量 $varTotal 访问。表达式 sum($varTotal/total_amount/itemtotal) 计算所有行项目的总金额,并给出一个可以在标头级别使用的单个 Total 值。

FF 反汇编器和汇编器命令行工具已用于测试 FF 架构是否没有任何问题

BizTalk 提供了几个管道工具,可帮助我们测试 FF 或 XML 管道是否正确生成或解析文件。有关更多信息,请参阅 此链接。在我们的案例中,我们使用 FFAsm.exe 将 FF XML 架构转换为实际的平面文件。此外,还使用 FFDAsm.exe 将实际的管道分隔平面文件转换为 FF XML 架构。

它们位于 BizTalk 安装路径>\SDK\Utilities\PipelineTools

FFAsm.exe  file_inp.xml –bs myBodySchema.xsd

上述命令从 file_inp.xml 生成格式为 myBodySchema.xsd 的平面文件。

FFDasm.exe file_in.txt –bs myBodySchema.xsd

此命令从平面文件 file_in.txt 生成格式为 myBodySchema.xsd 的 XML 文件。

结论

希望本文能解决 BizTalk 开发中遇到的一些常见问题并提供解决方案。如有任何建议/意见,请随时提出。

© . All rights reserved.