如何在解析XML时支持递归包含

3 投票
1 回答
1014 浏览
提问于 2025-04-16 05:17

我正在定义一个自己的 XML 模式,它支持一个额外的标签“insert_tag”。当解析到这个标签时,它应该在流中插入一个文本文件,然后继续解析。

这里有一个例子:

my.xml:

<xml> Something <insert_file name="foo.html"/> or another </xml>

我使用 xmlreader 的方式如下:

 class HtmlHandler(xml.sax.handler.ContentHandler):

    def __init__(self):
        xml.sax.handler.ContentHandler.__init__(self)

 parser = xml.sax.make_parser()
 parser.setContentHandle(HtmlHandler())

 parser.parse(StringIO(html))

我的问题是,如何将包含的内容直接插入到解析流中?当然,我可以通过不断插入包含的文本来递归构建未插入的文本,但这意味着我必须多次解析 XML。

我尝试用自己的流替换 StringIO(html),这个流允许在中间插入内容,但它不工作,因为 SAX 解析器是以缓冲的方式读取流的。

更新:

我确实找到了一种解决方案,虽然有点“黑科技”。它是基于以下流类构建的:

class InsertReader():
    """A reader class that supports the concept of pushing another
    reader in the middle of the use of a first reader. This may
    be used for supporting insertion commands."""
    def __init__(self):
        self.reader_stack = []

    def push(self,reader):
        self.reader_stack += [reader]

    def pop(self):
        self.reader_stack.pop()

    def __iter__(self):
        return self

    def read(self,n=-1):
        """Read from the top most stack element. Never trancends elements.
        Should it?

        The code below is a hack. It feeds only a single token back to
        the reader.
        """
        while len(self.reader_stack)>0:
            # Return a single token
            ret_text = StringIO()
            state = 0
            while 1:
                c = self.reader_stack[-1].read(1)
                if c=='':
                    break

                ret_text.write(c)
                if c=='>':
                    break

            ret_text = ret_text.getvalue()
            if ret_text == '':
                self.reader_stack.pop()
                continue
            return ret_text
        return ''

    def next(self):
        while len(self.reader_stack)>0:
            try:
                v = self.reader_stack[-1].next()
            except StopIteration:
                self.reader_stack.pop()
                continue
            return v
        raise StopIteration

这个类创建了一个流结构,限制了返回给流用户的字符数量。也就是说,即使 XML 解析器读取了 16386 个字符,这个类也只会返回到下一个 '>' 字符为止。由于 '>' 字符也表示标签的结束,我们可以在这个点将递归包含的内容注入到流中。

这个解决方案的“黑科技”之处在于:

  • 从流中一次读取一个字符的速度很慢。
  • 这隐含地假设了 SAX 流类读取文本的方式。

这个方法解决了我的问题,但我仍然希望能找到一个更优雅的解决方案。

1 个回答

0

你有没有考虑过使用 xinclude 呢?lxml 这个库本身就支持这个功能。

撰写回答