10.3. 缓冲节点查询

kgp.py 使用了多种技巧,对你进行 XML 处理而言它们或许有用。第一个就是,使用输入文档的结构稳定特征来构建节点缓冲。

一个语法文件定义了一系列的 ref 元素。每个 ref 包含了一个或者多个 p 元素,p 元素可以包含很多不同的东西,包括 xref。无论何时你遇到一个 xref ,都可以通过相同的 id 属性找到相对应的 ref 元素,并选择 ref 元素的子元素之一进行解析。(在下一部分中你将看到是如何进行这种随机选择的。)

如何构建语法:为最小的片段定义 ref 元素,然后通过 xref 定义“包含”第一个 ref 元素的 ref 元素,等等。然后,解析“最大的”引用并跟在每个 xref 后面,最后输出真实的文本。输出的文本依赖于你每次填充 xref 所做的(随机)决策,所以每次的输出都是不同的。

这种方式非常灵活,但是有一个不好的地方:性能。当你找到一个 xref 并需要找到相应的 ref 元素时,会遇到一个问题。 xrefid 属性,而你要找拥有相同 id 属性的 ref 元素,但是没有简单的方式做到这件事。较慢的方式是每次获取所有 ref 元素的完整列表,然后手动遍历并检视每一个 id 属性。较快的方式是只做一次然后以字典形式构建一个缓冲。

例 10.14. loadGrammar

    def loadGrammar(self, grammar):                         
        self.grammar = self._load(grammar)                  
        self.refs = {}                                       1
        for ref in self.grammar.getElementsByTagName("ref"): 2
            self.refs[ref.attributes["id"].value] = ref      3 4
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 函数。