使用 SAX 解析器确定是否在叶节点

Determining if at leaf node with SAX parser

使用org.xml.sax.helpers.DefaultHandler,你能确定你是否在endElement(String, String, String)内的叶节点吗?

或者您需要使用 DOM 解析器来确定吗?

让我们从一些基本定义开始:

An XML document is an ordered, labeled tree. Each node of the tree is an XML element and is written with an opening and closing tag.

(来自 here)。最重要的是:这意味着 XML 文件具有非常规则、简单的结构。例如,leaf 节点的定义就是:没有任何子节点的节点。

现在:只要 SAX 解析器遇到节点的 closing 标记,就会调用 endElement() 方法。假设您的 XML 具有有效内容,这也意味着解析器之前给了您相应的 startElement() 调用!

换句话说:确定您是否是 "ending" 叶节点所需的所有信息都可供您使用:

  • 你被告知哪些元素是 "started"
  • 你被告知哪些元素结束

举个例子:

<outer>
  <inner/>
</outer>

这将导致这样的序列 events/callbacks:

  • 事件:开始元素外层
  • 事件:内部元素开始
  • 事件:内部结束元素
  • 事件:外部结束元素

所以,"obviously",当你的解析器记住事件的历史,确定innerouter中的哪个是叶节点直截了当!

因此,答案是:不,您不需要 DOM 解析器。最后,DOM 是根据完全相同的信息构建的!如果 DOM 解析器可以推断出对象的 "scope",那么您的 SAX 解析器也可以。

但仅作记录:您仍然需要仔细实施跟踪 "started"、"open" 和 "ended" 标签的数据结构,例如正确确定此一:

<outer> <inner> <inner/> </inner> </outer>

表示两个非叶子节点(outer和第一个inner),和一个叶子节点(内部inner)。

从实现的角度来看,您可以仅使用一个布尔标志来执行此操作,跟踪元素是否是潜在的叶节点。每当您输入一个元素时,该标志将始终为真,但只有第一个实际的叶节点结束元素才会应用叶节点逻辑。

每当应用 startElement 时,都可以重复重置此标志。

如果多个叶节点处于同一级别,您将获得连续的 isLeafNode 标志集。

如果我们将 XML 想象成一个堆栈,就可以看出这背后的逻辑推理。 startElements 被压入堆栈。推送后第一个从堆栈中弹出的将是叶节点。随后的弹出不会是叶子,但如果执行另一次推送,则会重置。

private boolean isLeafNode = false;

public void startElement(String uri, String localName, String qName, Attributes attributes) {
    isLeafNode = true;
}

public void endElement(String uri, String localName, String qName) {
    if(isLeafNode) {
        //do leaf node logic
    }

    isLeafNode = false;
}

所以,对于下面的XML,叶节点如下

<foo>
    <bar>Leaf</bar>
    <baz>
        <bop>Leaf</bop>
        <beep>Leaf</beep>
        <blip>
            <moo>Leaf</moo>
        </blip>
    </baz>
</foo>