XML 问题: 管道流微格式

发表于:2007-05-25来源:作者:点击数: 标签:
计算中最优雅的抽象之一就是 UNIX 中的管道结构。管道允许通过把一个程序的输出作为另一个程序的输入而将一些小程序连接起来以实现重用,这些小程序只能做一件事,但是能做得很好。不幸的是,因此大部分 UNIX 管道是面向行处理的,不能直接用于 XML 处理。我
计算中最优雅的抽象之一就是 UNIX® 中的管道结构。管道允许通过把一个程序的输出作为另一个程序的输入而将一些小程序连接起来以实现重用,这些小程序只能做一件事,但是能做得很好。不幸的是,因此大部分 UNIX 管道是面向行处理的,不能直接用于 XML 处理。我们将考察一些现有的工具,这些工具是致力于解决该问题的很多尝试的成果。

流是一个与管道有关的概念:即管道操作的对象。流的思想是程序不需要等待所有的数据都就绪后才开始工作,这样程序就可以处理随时生成的数据、在网络上传输的数据或者不能全部装载到内存中的大文件。

(注意:下面的段落中将提到很多工具、规范和库。在本文最后的 参考资料 部分可以找到相关链接。)

为何要使用管道流?

管道和流技术在 UNIX 上取得了极大成功,在很多其他系统上的应用也获得了不同程度的成功。在多数编程语言和操作系统上能以这种或那种形式使用。在 XML 中,最常见的管道和流处理形式是 SAX Filter。本专栏的多数读者都知道,SAX 表示 Simple API for XML。这是一种基于流的 API,解析 XML 并在解析过程中发生重大事件时(比如打开或关闭元素)调用事件处理程序。因为 SAX 处理不需要维护状态或者构造庞大的数据结构(比如 DOM),通常用于操作大型数据集(很难全部装载到内存中)、需要快速处理或者稀疏处理(只选择部分 XML 使用而忽略其他部分)的 XML 任务。

在编程语言层(比如 Java™ InputStreamReader 类或者 SAX Filter)使用管道和过滤器的问题在于,必须在最底层工作,需要编写单调的、易出错的和重复性的代码。为了提升代码的抽象层次,同时要保持足够的灵活性以适应各种 XML 处理任务,人们开发了下面要讨论的不同库和工具。此外,将几种 XML 工具连接起来进行分阶段处理(管道)很多时候需要多次重复解析 XML。从一个阶段转到另一个阶段时最好将处理后的 XML 保存在内存中。

但首先我们来看看使用管道流处理库能够解决什么问题,既然用这种抽象方法处理 XML 非常好,到底能用来做什么呢?

XML 管道的一些常见用途包括:

  • 读取/解析 XML(无论在磁盘上、网络上、XMS 中或者其他地方)
  • 将非 XML 转化成 XML(比如将 reStructured Text 转化成 XHTML)
  • 验证(不论使用 DTD、XSchema、RelaxNG 还是 Schematron)
  • 组合 XML 片段(比如通过 XInclude 或 DITA Maps)
  • 转换(可能使用 XSLT)
  • 填充模板(用实际内容代替占位符)
  • 格式化以便显示或打印(通过 CSS 或 XSL-FO)
  • 序列化/写回 XML(磁盘、网络、CMS 或其他地方)
  • 用声明性语法将这些连在一起(通常也使用 XML)

注意,一个管道可能包含全部这些应用或者其中的任何一些,有的可能还要重复或者有所变化,也可能包含其他定制的成分。但这些似乎是 XML 处理的核心内容。并且像前面所述的那样,整个处理过程中管道应该保留解析后的 XML,而不需要在每个阶段反复解析。

比方说,可以用表示人和事件的零散标记组装成 Web 日程表。也可以反其道而行,通过扫描几个人的 Web 日程表将他们的安排收集起来确定适当的会议时间。本文将使用日程表和人员作为例子,但这种思路适合于任何能够用 XML 表达的东西(现在真是太多了)。





回页首


微格式运动

选择日程表作为例子的原因之一是现在兴起了一种 “微格式” 运动,即用现有的 XML 方言表示原来的规范,而不是重新发明轮子或者别的东西。它们关注的最常见的 XML 方言是 XHTML,使用 classlink 属性来增加人类可读(在网页中)和机器可读(通过在属性中包含针对机器的数据)的语义。这类微格式包括关于个人和组织的(基于 vCard 规范的 hCard)、日程表和事件(基于 iCalendar 规范的 hCalendar)、大纲和链接(XOXO)、标记和关键字(rel-tag)、审阅(hReview)、社会网络(XFN)、内容许可(rel-license)等等。本文主要讨论 hCalendar。

清单 1 显示了 hCalendar 微格式的一个例子:


清单 1. hCalendar 微格式
<div class="vevent">
            <abbr class="dtstart" title="20060322T0830-0800">
            March 22, 2006 - 08:30
            </abbr> -
            <abbr class="dtend" title="20060322T0900-0800">
            09:00
            </abbr> -
            <span class="summary">
            Dentist Appointment
            </span> - at
            <span class="location">
            Office on Pacific Ave.
            </span>
            <div class="description">
            Get permanent crown installed.
            </div>
            </div>

该例是用 hCalendar 创建程序生成的。Phil Dawes 在网站上提供了一个解析 hCalendar 和 hCard 的 python 库,在 hCalendar 页面上还可以找到更多工具和实现。使用 Greasemonkey 扩展的Firefox® 用户甚至能够找到从所访问的页面抽取各种微格式的 Greasemonkey 脚本。Upcoming.org 和 Eventful.com 在网站上用 hCalendar 格式提供事件和日程表。显然我们能找到大量使用微格式的数据来处理。





回页首


再回到管道和流

找到或者创建 XML 数据后如何建立一个管道来处理它呢?有多种方法,复杂程度各不相同。SAX Filters 作为一种底层的管道类型,常常被用作其他工具箱的基础或者灵感的来源。XMLGAWK 扩展了面向行的编程语言 GNU AWK,为了处理 XML,它不再面向行而改为面向元素。XmlRpcFilteringPipe 是 Lion Kimbro 开发的管道流工具,使用 XML-RPC 创建管道。NetKernel™ 是作为整套工具箱出现的,它采用了管道流的概念并将其推到极致。Apache Cocoon 是围绕着 “组件流” 这个概念建立的,在概念上和 XML 管道流类似,但是更加复杂。所有这些技术都提供了很好的方法,但是我想讨论两种在能力和简单性之间取得最佳平衡(对我而言)的项目,然后介绍几种有前途的开发项目。

首先是来自 Sean McGrath 的 XPipe。Sean 还开发了 Pyxie(现在到了 Pixie2),它将 XML 转化成适合于标准 UNIX 管道和过滤器处理的面向行的语言。此外,XPipe 开提供一组可扩展的 Java 组件。我认为 Sean 走对路了,但现在计划似乎失败了。网站有段时间没有更新了,Sean 在邮件中告诉我他的公司有一个商业版本需要继续开发,因此没有时间再作开放源代码的版本,如果再做的话会有很大的变化。我认为他采取了一种很好的方法,但我认为在保留基本思路的同时还可以更简单一点。

目前我看到的最有竞争力的是 Norm Walsh 的 Simple XML Pipeline(或者叫做 SXPipe)。它基本上包括本文开始列出的所有 XML 处理任务(这不是偶然的,这个列表就是以 SXPipe 的功能为基础的),具有良好的规范,很容易为其编写 XML。但有一些要求:它默认需要 Java 1.5,目前在 OS X 上只能得到开发人员预览版,而且这些阶段本身都是庞大的 Java 组件,使得系统扩展起来不像预期的那样容易,但与 SXPipe 所实现的功能相比这都是些小问题。除了简单的语法和处理模型外,SXPipe 本身非常简单(只有五个元素)并且是基于标准的(利用了 XPath 1.0、XML Namespaces、XML Base和 XSL Transformation,在适当的上下文中使用这些规范)。这种语言甚至比 W3C 注解(Norm 参与编辑)“XML Pipeline Definition Language” 更简单,但又保留了早期可以工作的基本能力和灵活性。清单 2 展示了一个 SXPipe 文档例子:


清单 2. SXPipe 文档
<pipeline xmlns="http://sxpipe.dev.java.net/xmlns/sxpipe/">
            <stage process="Read" input="dw-article.xml"/>
            <stage process="Validate" schema="dw-article-5.0.xsd"/>
            <stage process="Transform" stylesheet="dw-article-5.0.xsl"/>
            <stage process="Write" output="dw-article.html"/>
            </pipeline>

既简单又可爱。我的设想是能够用 Python 进行扩展。比方说,我可以很方便地增加将 Restructured Text 处理为 XML 的阶段作为管道的一部分,或者应用 Cheetah 模板。用 Python 实现 SXPipe 不会非常困难,但是要实现可移植性有些挑战,因为 Python 没有默认提供 XSL 或 XML 验证库。

管道流的前途是光明的。因为出于同样的目的出现了如此多的工具,这似乎表明确实存在对管道流的需要。无论最终以 SXPipe(比方说)作为标准,还是继续沿着自己的道路滚滚向前,管道和流仍将存在。从我喜爱的编程语言 Python 的角度来看,PullDom 已经作为标准库的一部分存在好几年了,并将在 Fredrik Lundh 的 ElementTree 库 2.5 版中进一步加强。我曾经试图使用 ElementTree 实现 SXPipe,但是它仍然没有转换或验证工具,对 XPath 的支持也很有限。Martijn Faassen 的 lxml 库用 Python 封装了功能强大,但难以使用的 libxml2 和 libxslt,公开了和 ElementTree 一样简单的接口以及 XInclude、完整的 XPath、XSLT 和各种形式的验证。如果该项目稳定下来,用 Python 创建较好的 SXPipe 实现就比较容易了。





回页首


结合在一起

现在已经有了一些微格式内容和建立管道的工具,接下来该做什么呢?我们来探讨几种可能性。这仅仅是一种思维实验,因此 URL 只作为例子并不真正指向什么地方。假设您是基本的家庭四口之家的一分子:Wanda(母亲)、Xathrus(父亲)、Yolanda(女儿)和 Zander(儿子)。每个人在自己的网页上都有一个日程安排表,格式为 http://example.com/calendar/[name]/[period],其中 period 可以是年、年-月、年-月-日或者某个自然语言短语,如 “tomorrow” 或 “next week”。现在需要定期地收集这些安排来导入 Apple® 的 iCal® 或 Mozilla® 的 Sunbird™ 这类程序。从 Web 上下载一个 XSLT 转换样式表把 hCalendar 转化成 iCalendar,称之为 “cal-transform.xsl”,然后使用 清单 3 中的源文档(source-calendar.xml):


清单 3. source-calendar.xml
            <calendar xmlns:xi="http://www.w3.org/2001/XInclude">
            <xi:include href="http://example.com/calendar/Wanda/today"/>
            <xi:include href="http://example.com/calendar/Xathrus/today"/>
            <xi:include href="http://example.com/calendar/Yolanda/today"/>
            <xi:include href="http://example.com/calendar/Zander/today"/>
            </calendar>
            

然后将管道 清单 4 应用于源文档:


清单 4. 添加到源文档
            <pipeline xmlns="http://sxpipe.dev.java.net/xmlns/sxpipe/">
            <stage process="Read" input="source-calendar.xml"/>
            <stage process="XInclude"/>
            <stage process="Transform" stylesheet="cal-transform.xsl"/>
            <stage process="Write" systemId="family-calendar.ics"/>
            </pipeline>
            

完成这些之后运行管道将得到可导入的单个日程表文件。我们来看看不同的用例。如果微格式推广开,应该能够从银行(帐户和信用证明)、杂货店(数字格式的收据)、甚至图书馆下载自己的数据。即使不关心其中大部分数据是否有效,但是仍然希望用模式验证银行数据。所有的数据源都希望转换数据以统一到一种格式。因此要发挥您的想像力(暂不考虑安全问题,毕竟这里讨论的是管道流)将这些不同的资源集中到一个流中,如 清单 5 所示:


清单 5. 单一流
            <pipeline xmlns="http://sxpipe.dev.java.net/xmlns/sxpipe/">
            <!-- Get my schedule for the day -->
            <stage process="Read" input="http://myjob.com/myappointments"/>
            <stage process="Transform" stylesheet="calendar-to-portal.xsl"/>
            <stage process="Write" systemId="work.xml"/>
            <!-- Do I have any books due back at the library? -->
            <stage process="Read" input="http://muni-library.gov/books-due"/>
            <stage process="Transform" stylesheet="library-to-portal.xsl"/>
            <stage process="Write" systemId="library.xml"/>
            <!-- Get latest transactions in my chequing account -->
            <stage process="Read" input="http://mybank.com/chequeing-acct"/>
            <stage process="Validate" schema="my-bank-acct.xsd"/>
            <stage process="Transform" stylesheet="chequing-to-portal.xsl"/>
            <stage process="Write" systemId="chequeing.xml"/>
            <!-- Get latest transactions on my credit card -->
            <stage process="Read" input="http://mybank.com/my-credit-acct"/>
            <stage process="Validate" schema="my-creditcard.xsd"/>
            <stage process="Transform" schema="creditcard-to-portal.xsl"/>
            <stage process="Write" systemId="creditcard.xml"/>
            <!-- Check the weather forecast -->
            <stage process="Read" input="http://localweather.com"/>
            <stage process="Transform" stylesheet="weather-to-portal.xsl"/>
            <stage process="Write" systemId="weather.xml"/>
            <!-- Create an XInclude document to glue it all together -->
            <stage process="Document" label="accumulator">
            <portal xmlns:xi="http://www.w3.org/2001/XInclude"
            xmlns:"http://www.example.com/portal">
            <xi:include href="work.xml"/>
            <xi:include href="library.xml"/>
            <xi:include href="chequeing.xml"/>
            <xi:include href="creditcard.xml"/>
            <xi:include href="weather.xml"/>
            </portal>
            </stage>
            <stage process="XInclude"/>
            <!-- Convert it all to HTML -->
            <stage process="Transform" stylesheet="portal-to-html.xsl"/>
            <!-- Viola! We have our own personal portal on the world -->
            <stage process="Write" systemId="my-daily-portal.html"/>
            </pipeline>
            





回页首


结束语

我看到似乎有人对最后一个例子有点怀疑,这是因为我还没有说明微格式和联合的结合点。大量当前的微格式内容被设计来(或者花样翻新为)流入新闻提要聚合程序,后者最初用于读取 RSS 格式的博客。流入聚合程序的不仅仅有 hCalendar,正如前一个例子中所提示的:已经出现了用于审阅和大纲、事件和人员的微格式,但是很快就会看到用于商业交易、过期图书、天气预报以及无法预料的其他事物的微格式。

除了微格式外,新出现的另一股力量是 Atom,它实际上是两种不同的规范,即 Atom 联合格式和 Atom API。与 Atom 的联合非常类似于与原来的 RSS 格式的联合,但是规范更明确(它是一个 IETF 标准)。不过这是下一期文章要讨论的内容了,我将探讨微格式和 Atom 联合的结合点。到时候,您的管道也许已经没有漏洞,流也干净了。

原文转自:http://www.ltesting.net