10.3. 缓存节点查找

kgp.py 采用了几种技巧,这些技巧在您的 XML 处理中可能有用,也可能没用。第一个技巧利用输入文档的一致结构来构建节点缓存。

语法文件定义了一系列 ref 元素。每个 ref 元素包含一个或多个 p 元素,这些元素可以包含许多不同的内容,包括 xref。每当您遇到 xref 时,您都需要查找具有相同 id 属性的对应 ref 元素,并选择该 ref 元素的其中一个子元素并对其进行解析。(您将在下一节中看到如何进行这种随机选择。)

您可以通过以下方式构建语法:为最小的部分定义 ref 元素,然后通过使用 xref 定义“包含”第一个 ref 元素的 ref 元素,依此类推。然后,您解析“最大”的引用并跟随每个 xref,最终输出真实的文本。您输出的文本取决于您每次填充 xref 时做出的(随机)决定,因此每次输出都不同。

这非常灵活,但有一个缺点:性能。当您找到一个 xref 并需要找到具有相同 id 属性的 ref 元素时,您就会遇到问题。xref 有一个 id 属性,您想找到具有相同 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 函数。