内容


使用 XML

使用 XI 将文本导入为 XML

现实数据怎么办?

Comments

系列内容:

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

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

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

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

为保持两个字母名称的传统,新项目名为 XI,它是“XML 导入(XML Import)”的缩写。XI 提供了一种将文本文档导入为 XML 文档的简单解决方案。将它与我在先前专栏文章中介绍的 XSLT Maker(XM)结合起来使用尤其方便,因为它允许我将文本文档作为发布环节中的一部分处理。更具体地讲,我期望导入一个用 XM 维护的网站中的地址列表。

很明显,有一些其它应用程序会用到 XI。在需要将文本文档转换成 XML 的任何场合,它都很有用。这可能包括电子商业和类似的项目。在本专栏文章中我将略微谈到这些案例。

XML 和其它数据

XML 在非常短的时间内证明了它在很多方面都取得了令人难以置信的成功。仅在 W3C 发布 XML 1.0 四年之后,就很难找到完全不知道 XML 的开发人员了。同样,还开发了数千个词汇表(或 XML Schema),甚至还开发了更多的应用程序。

在许多项目中,仍可以发现部分或所有数据最初不是 XML 格式。 最近的应用程序已将 XML 作为其主要的文件格式,而比较旧的应用程序依赖于其它格式, 因此您最终常常得到新旧格式混合的数据。

地址簿和网站

自从我最初在本专栏文章中发布了 XM 之后,我的公司就使用它来发布我们维护的几个网站。最近的一个项目完美地说明了 XML 是如何与其它文件共存的。该网站支持一个已经进行了好几年的项目。 经历了这几年,该项目已经积累了许多文档,大多数都是 Word 格式。 由于它们是压缩文档的一部分,所以我们简单地用 XM 目录读取(directory reading)来链接它们。

然而,我们仍需要将少数几个文件导入网站。其中的一个文件是项目参与者列表, 现在将它作为文本文档维护,类似于 清单 1(这里的姓名和地址都是虚构的)。

由于与本讨论不相关的许多理由,需要保留该列表并以那种格式加以维护,以在可以预见的未来使用。 我们还必须发布更新的参与者列表,最好是作为具有至电子邮件地址的超链接的 HTML 文档发布。一个解决方案是以 XML 格式复制该列表,但我们不太愿意这样做,因为该列表经常更改。 这会引起维护问题。

因为我们无法将原始文档直接提供给 XM,所以我们决定将它自动转换成 XML。 只要该过程是自动的,我们就可以保证始终提供最新的在线版本。作为附带好处, 它还为我开发该项目提供了某些灵感。

关于 XML 导入

幸运的是,这个问题对于我来说并不新奇。我作为顾问的大部分工作都是关于电子商业方面的。 我最先从事 EDI(电子数据交换(Electronic Data Interchange))方面的工作,它是最老的电子商业形式之一, 然后逐渐转到在线商店和 XML Web 服务。

您首先要学习电子商业的事情之一就是处理无数的文件。当典型的电子商业应用程序进行下列操作时, 它必须与公司中的每个应用程序进行相互操作:

  • 收集产品目录中的产品描述和定价信息
  • 可以收集仓库中的产品可用性信息
  • 与业务管理系统相互操作来处理订单并准备发票
  • 就付款和其它信息与财会系统通信

在企业对企业的交互中使用许多文件的情形甚至更加常见,在这种情形中,根据定义,您不仅要设法与您公司的后台办公系统(back-office)集成, 您还要与您伙伴的后台办公系统集成。

在理想情况下,所有相关信息都将保存在 DB2 数据库中,而且每个应用程序都有一个清晰的、定义良好的 API。 遗憾的是,事实是几乎总是不能以您最需要的格式来使用数据。

例如,您可能发现产品目录数据被锁定在专用数据库中, 而且已经用一个简单的 PIM 来维护客户列表,该 PIM 提供很少的机会来导出其数据库。 在其它情况下,后台办公系统可能已经在内部开发,而它过度增长已经不再适用于其原始设计了。

您也可能在该领域发现一些有用的用到 XI 的应用程序。然而,出于本专栏文章的目的,我坚持导入参与者列表。

导入和 XSL

近两年来,我使用了不同工具来处理不同解决方案(从高端解决方案到更便宜的解决方案)的这些文档。 我还参与编写了一些应用程序,我多次发现当 XSLT 与语法转换器结合使用时,它会提供非常有吸引力的解决方案。

我发现将导入过程分成以下两个步骤是有利的:语法转换和数据结构的重新组织。 语法转换采用文本信息并用最简单的 XML 结构封装它。 一般情况下,产生的 XML 文档十分接近于原始文档。在大多数情况下,它与用 XML 标记替换定界符一样简单。

第二步是使用 XSLT 并用目标词汇表转换这个原始的 XML 文档。这种分成两步的方法做得相当好,并提供下列好处:

  • 代码被组织在两个模块中,因此更容易维护
  • 对不同的任务使用最好的工具来编写每个模块: 系统编程语言(如 Java)对于语法转换来说是理想的,脚本编制语言(象 XSLT)对于重新组织是最佳的
  • 比起用 Java 编码,编写样式表相对来说比较简单,所以您可以将这个工作分配给较低级的小组成员
  • 通常可以将语法转换器和样式表用于和重用于不同文档

但是,对于该项目,文本文档似乎有一种非常特别的格式,所以不太可能重用。 然而,新的 JDK 1.4 添加了对正则表达式(regex)的支持,我认为这是构建更通用的解决方案的好方法。

如果您不熟悉正则表达式,则参考 O'Reilly 出版的 Mastering Regular Expressions(请参阅 参考资料)。简而言之,正则表达式描述字符串模式。通过使用正则表达式引擎,很容易测试出字符串是否与模式匹配。 这对于验证输入或分隔与模式匹配的文档很有用。

高级规范

为了更好地理解 XI 的一切,请查看清单 2,它描述了解释清单 1 的正则表达式。最终,XI 使用这个描述中的表达式,将清单 1 转换成清单 3;后者是 XML 文档。

清单 2. XI 描述规则
<?xml version="1.0">
<xi:rules version="1.0"
          xmlns:xi="http://ananas.org/2002/xi/rules"
          xmlns:an="http://ananas.org/2002/sample">
<xi:ruleset name="an:address-book">
   <xi:match name="an:alias"
             pattern="^alias "([^"]*)" (\S*)$">
      <xi:group name="an:id"/>
      <xi:group name="an:email"/>
   </xi:match>
   <xi:match name="an:note"
             pattern="^note "([^"]*)" (\S*)$">
      <xi:group name="an:id"/>
      <xi:group name="an:fields"/>
   </xi:match>
   <xi:error msg="unknown line type"/>
</xi:ruleset>
<xi:ruleset name="an:fields">
   <xi:match name="an:field"
             pattern="<(\s):(\s)>">
      <xi:group name="an:key"/>
      <xi:group name="an:value"/>
   </xi:match>
</xi:ruleset>
</xi:rules>

注:我创建了清单 1 和清单 2 来帮助自己理解 XI 是如何工作的,所以把它们作为说明示例, 而不必作为最终文档。当我开发 XI 时,我可能会发现我需要对这些文档做一些小的修正。

清单 3. XI 将要生成的样本文档
<?xml version="1.0"?>
<an:address-book xmlns:an="http://ananas.org/2002/sample">
   <an:alias>
      <an:id>jdoe</an:id>
      <an:email>jdoe@xmli.com</an:email>
   </an:alias>
   <an:note>
      <an:id>jdoe</an:id>
      <an:fields>
         <an:field>
            <an:key>country</an:key>
            <an:value>US</an:value>
         </an:field>
         <an:field>
            <an:key>zip</an:key>
            <an:value>45202</an:value>
         </an:field>
         <an:field>
            <an:key>state</an:key>
            <an:value>OH</an:value>
         </an:field>
         <an:field>
            <an:key>city</an:key>
            <an:value>Cincinnati</an:value>
         </an:field>
         <an:field>
            <an:key>address</an:key>
            <an:value>34 Fountain Square Plaza</an:value>
         </an:field>
         <an:field>
            <an:key>name</an:key>
            <an:value>John Doe</an:value>
         </an:field>
      </an:fields>
   </an:note>
   <an:alias>
      <an:id>jsmith</an:id>
      <an:email>jsmith@worth-it.com</an:email>
   </an:alias>
   <an:note>
      <an:id>jsmith</an:id>
      <an:fields>
         <an:field>
            <an:key>first</an:key>
            <an:value>Jack</an:value>
         </an:field>
         <an:field>
            <an:key>last</an:key>
            <an:value>Smith</an:value>
         </an:field>
         <an:field>
            <an:key>name</an:key>
            <an:value>Jack Smith</an:value>
         </an:field>
      </an:fields>
   </an:note>
   <an:alias>
      <an:id>pdupont</an:id>
      <an:email>pdupont@pineapples.net</an:email>
   </an:alias>
   <an:note>
      <an:id>pdupont</an:id>
      <an:fields>
         <an:field>
            <an:key>name</an:key>
            <an:value>Pierre Dupont</an:value>
         </an:field>
      </an:fields>
   </an:note>
</an:address-book>

我已经设法尽可能使清单 3 中的格式保持简单。描述由一个或多个规则集(ruleset)组成。每个规则集指定输入必须匹配的模式列表。XI 将对照其中的每种模式测试输入, 并应用与第一个匹配它的输入关联的描述。

在模式内,圆括号表示一个组(group),如 清单 2中所示。每个组都可以与不同的 XML 元素匹配。

当模式匹配时,XI 或者生成一个或多个 XML 元素或者切换到另一个规则集。 这里的想法是必须进一步分解某些模式。 清单 1 中以 note "jdoe" 开始的 note 行是一个完美的示例。note 行包含可变的字段数,一旦我们用一个正则表达式标识了这样一行, 我们就需要使之在字段中分解。

整个 清单 2 中属性是一致的:name 属性表示输出中的 XML 元素, 它应用于 ruleset、match 和 group;call 属性表明必须使用另一个规则集进一步分解组。

故意限制了这种格式。例如,XI 仅生成元素 ― 而从不生成属性。它强制您将元素与每个 ruleset、match 和 group 关联。我的目的是尽可能使这种格式(和 XI)保持简单。与设法提出更巧妙的描述语言相比, 重新组织文档、将元素更改成属性并在 XSLT 中跳过和插入标记就方便多了。

在这种情况下,产生格式为 Docbook 的 XI 输出是类似于 清单 4 中所示的样式表的职责。 注:该样式表还没有进行优化;它仅作说明之用。

XPaths 和 XSLT 2.0

XI 不同于提议的将正则表达式添加到 XPath 2.0(因而成了 XSLT)。它也不同于 Simon St Laurent 的正规分段(Regular Fragmentation)库(请参阅 参考资料)。 就我对初步草案的理解,XPath 2.0 似乎将使用模式来分解元素。然而,仍然假设输入文档已经是 XML 文档。

相反,XI 使用正则表达式来将文本文档转换成 XML。 如果有什么不同的话,那就是这两个解决方案是互补的: 用 XI 准备 XML 文档然后将之传递给将使用 XPath 2.0 来选择特定信息位的样式表可能会不错。

分析

本节用 XM 的简要分析来补充上述讨论。

实现 XMLReader

在我最早的 XML 转换尝试中,创建了临时文件,我把它们传递到 XSLT 转换器。 遗憾的是,这会将文档写入磁盘并重读它。稍后,我学会了跳过中间解析过程并直接从转换器生成 SAX 事件。 在开发 XM 期间,我介绍了使用 DirectoryReader 的技巧。

最近我发现,实现 XMLReader 接口甚至更有益处。当用 XSLT 对文档进入后处理时,这尤其正确, 因为 javax.xml.transform.sax.SAXSource 接受 XMLReader 作为参数。

类图

下面的图 1 是 XI 的类图。主类是 XIReader,它实现 XMLReader。该类负责根据定义解析文本文档、应用正则表达式并生成适当的 SAX 事件。

因为 JDK 包含正则表达式库,所以我想实现这个类不会有任何困难。 您必须使用 setProperty() 向 XIReader 提供文档描述(类似于 清单 2)。 它使用 RulesHandler 来解码文档描述。由于 RulesHandler 是一种 SAX 内容处理程序,所以我认为 HC 便于使用。您将注意到从 HCHandler 继承的类。

最后,您将发现具有文档描述的几个类:清单 2 中的 Error、Group、Ruleset 和 Match 标记在图 1 中都有与之相应的类。我仍在考虑是否需要 Rule 元素。如果我选择引入一个 Rule 元素,那么它不应影响图 1 的任何主要方面。

图 1. XI 的类图
类图
类图

所有类都在 org.ananas.xi 包中定义。

下一步做什么

本专栏文章包含了 XI 的分析和需求。我们将在下一篇专栏文章中使用 XIReader 的第一版来实现该项目。照常,代码将遵循开放源码许可证发布。


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
  • 通过单击本文顶部或底部的 讨论来参与本文的 论坛
  • 了解电子商业开发人员如何长期使用转换工具(如 IBM WebSphere Data Interchange for Multiplatform)来处理各种格式的文档。 它不需要 XSLT。 http://www-3.ibm.com/software/ts/datainterchange/
  • 获取 GoXML Transform工具,它允许您以 XML 格式导入文档。它不需要 XSLT。
  • 下载可用来将文本文档转换成 XML 的 XML Convert 应用程序。 通常要用 XSLT 来补充它。
  • 请阅读 Benoit Marchal 所有的 使用 XML 文章。
  • 请阅读关于应该支持正则表达式的 XPath 2.0文章。
  • 学习有关 Simon St Laurent提出的正规分段(Regular Fragmentation)来分割 XML 元素的知识。
  • 有关正则表达式的信息,请阅读参考书 Mastering Regular Expressions(由 Jeffrey E. F. Friedl 著,O'Reilly 于 1997 年出版)。

评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=22197
ArticleTitle=使用 XML: 使用 XI 将文本导入为 XML
publish-date=04012002