 | 级别: 中级 Uche Ogbuji (uche.ogbuji@fourthought.com), 首席顾问, Fourthought, Inc.
2003 年 7 月 01 日 SAX 过滤器允许您从简单的独立模块构造复杂的 XML 处理。在这篇技巧文章中,Uche Ogbuji 介绍了这一重要的 XML 处理技术。
Simple API for XML(SAX)是一种非常有效的 XML 处理方法。在 SAX 处理中,解析器向应用程序传递一个代表 XML 内容的事件流。SAX 的一个重要方面在于用户创建
SAX 过滤器的能力,这种过滤器接受 SAX 事件流,然后传递经过修改的流。例如,有些 XML 化的 HTML 使用了不好的 HTML 习惯(如
<center> ),您可能会使用 SAX 过滤器来将其作为输入,然后传递正确的 XHTML 形式(如
<div style="align: center"> )。随后可以非常方便地将这类过滤器重用于各种应用程序,因为它执行的任务单一且专一,并有意和系统上行分开,也使下行和过滤器分开。
如果您不熟悉 SAX,那么请参阅
参考资料中所提及的一些入门资料。
一个选择英文部分的 SAX 过滤器
XML 1.0 允许您使用
xml:lang 属性逐个元素地指定元素内容中所使用的语言(请在
参考资料中参阅我以前的技巧文章“Localization within a document format”,以获取更多这方面的信息)。我将在这里用 Python 创建一个 SAX 过滤器,它将所有已知的非英文内容剥去;换句话说,过滤器保留所有不带
xml:lang 指定或带有以
en 打头的指定的内容。
大多数基于对象的 SAX 系统中的 SAX 过滤器实现都是作为专门的处理程序类。所有 SAX 处理程序都从上行源接受 SAX 事件,而这个上行源可能直接就是 XML 解析器。SAX 过滤器也是 SAX 处理程序类,但它也有自己的特点,即它所做的操作是通过对给定的实例调用适当的方法来生成进一步的 SAX 事件,这个实例就是过滤器链中的下行 SAX 处理程序。
清单 1是一个 SAX 过滤器实例。
清单 1. 一个除去非英文内容的过滤器(en-filter.py)
import xml.sax
from xml.sax.saxutils import XMLFilterBase, XMLGenerator
#Define constants for the two states we care about
ALLOW_CONTENT = 1
SUPPRESS_CONTENT = 2
class EnglishOnlyFilter(XMLFilterBase):
def __init__(self, upstream, downstream):
XMLFilterBase.__init__(self, upstream)
self._downstream = downstream
return
def startDocument(self):
#Set the initial state, and set up the stack of states
self._state = ALLOW_CONTENT
self._state_stack = [ALLOW_CONTENT]
return
def startElement(self, name, attrs):
#Check if there is any language attribute
lang = attrs.get('xml:lang')
if lang:
#Set the state as appropriate
if lang[:2] == 'en':
self._state = ALLOW_CONTENT
else:
self._state = SUPPRESS_CONTENT
#Always update the stack with the current state
#Even if it has not changed
self._state_stack.append(self._state)
#Only forward the event if the state warrants it
if self._state == ALLOW_CONTENT:
self._downstream.startElement(name, attrs)
return
def endElement(self, name):
self._state = self._state_stack.pop()
#Only forward the event if the state warrants it
if self._state == ALLOW_CONTENT:
self._downstream.endElement(name)
return
def characters(self, content):
#Only forward the event if the state warrants it
if self._state == ALLOW_CONTENT:
self._downstream.characters(content)
return
if __name__ == "__main__":
parser = xml.sax.make_parser()
#XMLGenerator is a special SAX handler that merely writes
#SAX events back into an XML document
downstream_handler = XMLGenerator()
#upstream, the parser, downstream, the next handler in the chain
filter_handler = EnglishOnlyFilter(parser, downstream_handler)
import sys
#The SAX filter base is designed so that the filter takes
#on much of the interface of the parser itself, including the
#"parse" method
filter_handler.parse(sys.argv[1])
|
Python 提供了一个实用程序类 —
XMLFilterBase ,可以从这个类派生 SAX 过滤器。我将
EnglishOnlyFilter 定义为过滤器,它获取一个上行 SAX 事件源(解析器或另一个过滤器)以及一个下行 SAX 过滤器或其它处理程序。许多 SAX 处理程序都充当状态机,即它们根据进入的事件流管理一些变量,并根据这些变量更改行为。
EnglishOnlyFilter 被设置为两种状态之一:一种状态是内容被传递给下行处理程序,而另一种状态是内容被阻截。这种状态在
self._state 实例变量中标记。状态最初设置为
ALLOW_CONTENT ,如果过滤器碰到一个表示非英文(可以根据标准语言代码规则检查值的前两个字符来做到这点)的
xml:lang 属性,状态就会改为
SUPPRESS_CONTENT 。
XML 语言规范是
限定了作用域的。请参阅
清单 2这个示例,可以从中看出这到底指的是什么。
清单 2. 带有多种语言的样本 XML 文件(listing2.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu>
<item id="A" xml:lang="en">Orange juice</item>
<item id="A" xml:lang="es">Jugo de naranja</item>
<item id="B" xml:lang="en">Toast</item>
<item id="B" xml:lang="es">Pan tostada
<note xml:lang="en">Wheat bread only, please</note>
</item>
</menu>
|
在上面的示例中,字符串“Pan tostada”位于带有属性
xml:lang="es" 的元素的作用域内,因此它被标记为西班牙语字符串。但是,整个
note 元素被标记为英文内容,因为它的
xml:lang="en" 属性覆盖了前面的属性。这种作用域限定要求我在 SAX 过滤器中维持一个状态堆栈,即
self._state_stack 实例变量。准确地说,
self._state_stack 变量使得
self._state 不再是必需的 — 我本可以从堆栈顶部读取当前状态 — 但是为了更加清晰,我还是将它留下了。对这个样本 XML 运行过滤器代码会产生下面的输出。
$ python en-filter.py listing2.xml
<menu>
<item xml:lang="en" id="A">Orange juice</item>
<item xml:lang="en" id="B">Toast</item>
<note xml:lang="en">Wheat bread only, please</note>
</menu>
|
结束语
SAX 的速度已经很快了,而 SAX 过滤器又增加了一些灵活性。随着您越来越多地使用 SAX,您可能会发现您有了一个给人留下深刻印象的 SAX 过滤器库,它可以用于各种处理任务。
参考资料
关于作者
对本文的评价
|  |