10.3. 查询缓冲节点
kgp.py
使用了多种技巧,在你进行 XML 处理时,它们或许能派上用场。第一个就是,利用输入文档的结构稳定特征来构建节点缓冲。
一个语法文件定义了一系列的 ref
元素。每个 ref
包含了一个或多个 p
元素,p
元素则可以包含很多不同的东西,包括 xref
。对于每个 xref
,你都能找到相对应的 ref
元素 (它们具有相同的 id
属性),然后选择 ref
元素的子元素之一进行解析。(在下一部分中你将看到是如何进行这种随机选择的。)
语法的构建方式如下:先为最小的片段定义 ref
元素,然后使用 xref
定义“包含”第一个 ref
元素的 ref
元素,等等。然后,解析“最大的”引用并跟着 xref
跳来跳去,最后输出真实的文本。输出的文本依赖于你每次填充 xref
时所做的 (随机) 决策,所以每次的输出都是不同的。
这种方式非常灵活,但是有一个不好的地方:性能。当你找到一个 xref
并需要找到相应的 ref
元素时,会遇到一个问题。xref
有 id
属性,而你要找拥有相同 id
属性的 ref
元素,但是没有简单的方式做到这件事。较慢的方式是每次获取所有 ref
元素的完整列表,然后手动遍历并检视每一个 id
属性。较快的方式是只做一次,然后以字典形式构建一个缓冲。
例 10.14. loadGrammar
def loadGrammar(self, grammar):
self.grammar = self._load(grammar)
self.refs = {}
for ref in self.grammar.getElementsByTagName("ref"):
self.refs[ref.attributes["id"].value] = ref
[1] | 从创建一个空字典 self.refs 开始。 |
[2] | 正如你在第 9.5 节 “搜索元素”中看到的,getElementsByTagName 返回所有特定名称元素的一个列表。你可以很容易地得到所有 ref 元素的一个列表,然后遍历这个列表。 |
[3] | 正如你在第 9.6 节 “访问元素属性”中看到的,使用标准的字典语法,你可以通过名称来访问个别元素。所以,self.refs 字典的键将是每个 ref 元素的 id 属性值。 |
[4] | self.refs 字典的值将是 ref 元素本身。如你在第 9.3 节 “XML 解析”中看到的,已解析 XML 文档中的每个元素、节点、注释和文本片段都是一个对象。 |
只要构建了这个缓冲,无论何时你遇到一个 xref
并且需要找到具有相同 id
属性的 ref
元素,都只需在 self.refs
中查找它。
例 10.15. 使用 ref
元素缓冲
def do_xref(self, node):
id = node.attributes["id"].value
self.parse(self.randomChildElement(self.refs[id]))
你将在下一部分探究 randomChildElement
函数。