跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

使用 XML: 把文件映射成 SOAP 请求,第 2 部分

使用 XML 和 XSL 实现分析

Author photo
Benoit Marchal 是一位比利时籍顾问。他是 XML by Example, Second Edition 以及其他几本 XML 书籍的作者。Benoit 能够为您的 XML 项目提供帮助。可以通过 bmarchal@pineapplesoft.com或者他的个人站点 marchal.com和他联系。

简介: 许多应用程序正在升级以适应电子商务交易的需要。在上一篇专栏文章中,Benoit Marchal 分析了遗留数据,并说明如何映射到目前发展水平的 SOAP 请求中。在第 2 部分中,他讨论了实现这些分析所需要的 XML 和 XSL 代码。请您在本文的 讨论论坛 上与作者和其他读者交流您的想法。

发布日期: 2004 年 2 月 01 日
级别: 中级
访问情况 : 1352 次浏览
评论: 


在 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 数据模型

同样是在第 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 请求没有什么意义。

header/line 关系的变化

header 和 line 之间的关系有两种变化很常见。第一种变化是没有 header 记录——header 信息在每一行中重复。处理这种情况的最佳方法是 Muenchian 算法(请参阅 参考资料)。

另一种变化是在 line 记录中不重复订单引用,而利用位置联系 header 和 line(新的 header 记录表示新订单的开始,在出现下一个 header 之前,所有的 line 记录都属于该订单)。要处理这些文件,您必须从侧面遍历它们,每次一个记录,如技巧文章 "Recurse, not divide to conquer" 中所述(请参阅 参考资料)。

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 电子商务。


参考资料

关于作者

Author photo

Benoit Marchal 是一位比利时籍顾问。他是 XML by Example, Second Edition 以及其他几本 XML 书籍的作者。Benoit 能够为您的 XML 项目提供帮助。可以通过 bmarchal@pineapplesoft.com或者他的个人站点 marchal.com和他联系。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

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

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。