使用 XML

把文件映射成 SOAP 请求,第 2 部分

使用 XML 和 XSL 实现分析

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 使用 XML

敬请期待该系列的后续内容。

此内容是该系列的一部分:使用 XML

敬请期待该系列的后续内容。

在 2001 年,我向一位多疑的客户示范了 Web 服务可以很容易地现场部署。目的是用 Web 服务代替客户的大部分行政文书工作,从而“加强”其供给链。具有讽刺意味的是,测试中要将 Web 服务部署在客户的供应者一方,其中大多数是小型公司(15 到 50 名雇员)。

在这种情况下,使用传统的 RPC 基本上是不可能的——这就是我的客户持怀疑态度的原因。我建议,我们把 RPC 请求看作是一个 XML 文档,并使用 XML 编程而不是 RPC 编码构造调用。经过两天的测试,我们证明我的办法是可行的。

前几篇专栏文章所介绍的轻量级 XML 客户机就包含了同样的思想。在 上一篇专栏文章中,我说明了如何分析文件输出,并把它转换成 RPC 请求。这个月中我将介绍实际的编程。

分析小结

回顾本文的第 1 部分,我们的最终目标是把清单 1 所示的这种遗留数据转化成 SOAP 请求。请求有一个方法, processOrder() ,它以 Order 对象数组作为参数。

清单 1. 遗留文件示例
HDRAZ5251029200309252003  1899.00
HDRAZ5281029200310272003   149.95
LINWEBAZ525THPRE     IBM ThinkPad R Series Economy          1099.00
LINDITAZ525THPRV     IBM ThinkPad R Series Value         2   400.00
LINWEBAZ528BKXBE     XML by Example                      5    29.99

Order 对象在图 1 中用 UML 表示:

图 1. SOAP 数据模型
SOAP 数据模型
SOAP 数据模型

同样是在第 1 部分,您分析了这些数据并指定了两种形式之间的映射,如表 1 所示:

表 1. 分析结果

SOAP 元素导出字段说明
Order/ReferenceHDR_ORFF-
Order/DateHDR_ODTE转化成 xsd:date 格式
Order/Buyer-"Example Corp."
Order/Address-"Example Street 5, 45202 Cincinnati, OH"
Order/TotalHDR_OTOT-
Item/CodeLIN_OCOD-
Item/DescriptionLIN_ODES-
Item/PriceLIN_OPRI-
Item/QuantityLIN_OQTY如果没有则为 1

开发样式表

一旦成功完成了映射分析,编写相应的 XI 规则和 XSLT 样式表就很简单了。如果您还不熟悉 XI,它使用 正则表达式(regex)预处理文本文件并转化成 XML。预处理的结果作为 XSL 处理程序的输入,后者生成最终的 XML 文档。

之所以需要进行预处理是因为 XSLT 处理程序只接受 XML 文档作为输入。

编写正则表达式

预处理程序是用一组正则表达式(称为 规则集(ruleset))。每个正则表达式都由一个匹配元素表示。预处理程序尝试对规则集中的一个正则表达式匹配源文档,如果由一个正则表达式匹配,预处理程序就生成一个 XML 元素,后面跟着更多的元素,与正则表达式中每一组对应。

在 XI 的第一个版本中,您应该通过与匹配元素相关的模式属性指定正则表达式。但是,实践证明对于很长的正则表达式这样太复杂了(很难确定 group 元素与正则表达式的关系)。为了简化编码,最新的 XI 版本允许您直接把正则表达式的一部分与 group 元素关联。一个新的 filler 元素包含不属于组的那部分正则表达式(即不生成 XML 标签)。预处理程序把所有的部分连接起来形成给定匹配元素的正则表达式。

清单 2 包含 清单 1 中文档的一个 XI 规则集和测试用的 XSLT 样式表。两个记录( HDRLIN )转换成两个匹配元素。因为字段长度是固定的,正则表达式非常简单,采用 .{5} 这样的形式,其含义是“任意字符,重复 5 次”。

清单 2. 编写 XI 规则
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xi="http://ananas.org/2002/xi/rules"
                xmlns:o="http://ananas.org/2003/order"
                version="1.0">
<xi:rules version="1.0"
          defaultPrefix="o"
          targetNamespace="http://ananas.org/2003/order"
          defaultSpace="trim">
   <xi:ruleset name="export">
      <xi:match name="header">
         <xi:filler pattern="^HDR"/>
         <xi:group  pattern=".{5}" name="ORFF"/>
         <xi:group  pattern=".{8}" name="ODTE"/>
         <xi:filler pattern=".{8}"/>
         <xi:group  pattern=".{9}" name="OTOT"/>
         <xi:filler pattern="$"/>
      </xi:match>
      <xi:match name="line">
         <xi:filler pattern="^LIN"/>
         <xi:filler pattern=".{3}"/>
         <xi:group  pattern=".{5}"  name="ORFF"/>
         <xi:group  pattern=".{5}"  name="OCOD"/>
         <xi:filler pattern=".{5}"/>
         <xi:group  pattern=".{35}" name="ODES"/>
         <xi:group  pattern=".{2}"  name="OQTY"/>
         <xi:group  pattern=".{9}"  name="OPRI"/>
         <xi:filler pattern="$"/>
      </xi:match>
   </xi:ruleset>
</xi:rules>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
   <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Filler 适用于不作为 SOAP 请求一部分的那些字段,比如订单创建日期。在导出文件中为其他字段给出了与其名称匹配的 XML 名称(比如, HDR_ORFF 成了 ORFF )。

注意 rules 元素上 defaultSpace 属性的使用。它自动从字段数据中去掉多余的空格。固定长度的文件总是有大量不需要的空格,要求 XI 预处理程序清楚它们可以节约时间。

为了测试正则表达式,样式表只是不加改变地复制其输入。因为样式表的输入是预处理程序的输出,样式表可以有效地输出预处理程序生成的 XML 文档。毫无疑问,这是一种保留预处理程序输出结果的复杂语法。

“全盘复制”样式表只有一个模板,是我从 XSLT 推荐标准中直接复制过来的。

使用 清单 2中的样式表验证正则表达式。除非预处理程序能够准确地从导出文件中抽取所有相关的信息,否则继续尝试建立 SOAP 请求没有什么意义。

SOAP 编码

关于 SOAP 编码,在 上一期已经讨论了很多。简而言之,过程调用、类以及它们的变量都变成了 XML 元素。编码中最复杂的部分是数组,需要两个特殊属性(在 SOAP 1.1 中):

  • xsi:type 属性,它的值必须是 soapenc:Array
  • soapenc:arrayType 属性,它的值是一个数组声明,采用了类似 Java 语言的语法——即元素类型后面跟着放在方括号内的数组大小(比如, xsi:int[5] 或者 po:Order[3]

SOAP 1.2 中的数组编码发生了变化。SOAP 1.2 基本上放弃了该属性,而代之以可选的 soapenc:arraySize 属性。我使用的服务器(Axis 1.1)仍然只能识别 SOAP 1.1。此外,目前部署的多数 SOAP 服务器都实现了 SOAP 1.1。

无论使用什么 SOAP 版本,过程调用都包装在 SOAP EnvelopeBody 元素中。

XSLT 编码

XSLT 样式表负责格式化 SOAP 请求。因为服务器需要一个 Order 对象数组,而每个 Order 可以包含一个或多个 Item 对象,在样式表中有两个循环。第一个循环是关于 HDR 记录,第二个循环则是 LIN 记录。

清单 3 给出了样式表(XI 规则集和 清单 2相同):

清单 3. 最终的样式表
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xi="http://ananas.org/2002/xi/rules"
                xmlns:o="http://ananas.org/2003/order"
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:op="http://ananas.org/2003/order-processor"
                xmlns:order="http://ananas.org/2003/order-processor/order"
                version="1.0">
<xi:rules version="1.0"
          defaultPrefix="o"
          targetNamespace="http://ananas.org/2003/order"
       defaultSpace="trim">
   <xi:ruleset name="export">
      <xi:match name="header">
         <xi:filler pattern="^HDR"/>
         <xi:group  pattern=".{5}" name="ORFF"/>
         <xi:group  pattern=".{8}" name="ODTE"/>
         <xi:filler pattern=".{8}"/>
         <xi:group  pattern=".{9}" name="OTOT"/>
         <xi:filler pattern="$"/>
      </xi:match>
      <xi:match name="line">
         <xi:filler pattern="^LIN"/>
         <xi:filler pattern=".{3}"/>
         <xi:group  pattern=".{5}"  name="ORFF"/>
         <xi:group  pattern=".{5}"  name="OCOD"/>
         <xi:filler pattern=".{5}"/>
         <xi:group  pattern=".{35}" name="ODES"/>
         <xi:group  pattern=".{2}"  name="OQTY"/>
         <xi:group  pattern=".{9}"  name="OPRI"/>
         <xi:filler pattern="$"/>
      </xi:match>
   </xi:ruleset>
</xi:rules>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="o:export">
<soapenv:Envelope>
 <soapenv:Body>
  <op:processOrder 
           soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <orders xsi:type="soapenc:Array" 
           soapenc:arrayType="order:Order[{count(o:header)}]">
    <xsl:for-each select="o:header">
     <order>
      <reference><xsl:apply-templates select="o:ORFF"/></reference>
      <date><xsl:apply-templates select="o:ODTE"/></date>
      <buyer>Example Corp.</buyer>
      <address>Example Street 5, 45202 Cincinnati, OH</address>
      <xsl:variable name="orff" select="o:ORFF"/>
      <items xsi:type="soapenc:Array"
           soapenc:arrayType="order:Item[{count(../o:line[o:ORFF = $orff])}]">
       <xsl:for-each select="../o:line[o:ORFF = $orff]">
        <item>
         <code><xsl:apply-templates select="o:OCOD"/></code>
         <description><xsl:apply-templates select="o:ODES"/></description>
         <price><xsl:apply-templates select="o:OPRI"/></price>
         <quantity><xsl:apply-templates select="o:OQTY"/></quantity>
        </item>
       </xsl:for-each>
      </items>
      <total><xsl:apply-templates select="o:OTOT"/></total>
     </order>
    </xsl:for-each>
   </orders>
  </op:processOrder>
 </soapenv:Body>
</soapenv:Envelope>
</xsl:template>
<xsl:template match="o:ODTE">
<xsl:value-of select="substring(.,5,4)"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="substring(.,1,2)"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="substring(.,3,2)"/>
</xsl:template>
<xsl:template match="o:OQTY[not(text())]">
<xsl:text>1</xsl:text>
</xsl:template>
</xsl:stylesheet>

主模板和 o:export 匹配,这是预处理程序生成的第一个元素。它为 SOAP 信封、体和过程调用创建元素。然后是对 header 记录的循环。样式表使用 count() 函数计算数组大小。根据映射表编写字段(reference、date、buyer、address 和 total)。

第二个循环写 items 数组。为了把 line 和相应的订单联系起来,样式表使用了订单号,如条件 o:line[o:ORFF = $orff] 中所示。

注意,这里使用了一个变量( $orff )保存 header 引用。否则因为名称相同,XPath 就会把 header 引用与 item 引用混淆了。

最后,要注意 xsl:apply-templates 指令的使用,它检索字段值。大部分字段都由默认规则处理,即复制原始数据,但是 date 和 quantity 字段有专门的模板,分别用于重新格式化日期和提供丢失的信息。

喜欢 SOAP 的另一个理由

如这一系列文章所说明的那样,SOAP 和其他 RPC 标准相比有一个独有的特点:使用 XML 的文本表示。因为有更多的工具,XML 使 SOAP 比其他 RPC 标准更容易访问。

轻量级 XML 客户机,如本系列文章中所介绍的客户机,紧紧抓住这种增强的灵活性,可以为那些无力承担定制 RPC 和 Java 编程的公司部署 B2B 电子商务。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=20873
ArticleTitle=使用 XML: 把文件映射成 SOAP 请求,第 2 部分
publish-date=02012004