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

Posted

技术标签:

【中文标题】使用 SAX 解析器确定是不是在叶节点【英文标题】:Determining if at leaf node with SAX parser使用 SAX 解析器确定是否在叶节点 【发布时间】:2019-09-03 15:25:39 【问题描述】:

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

或者您是否需要使用 DOM 解析器来确定这一点?

【问题讨论】:

【参考方案1】:

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

XML 文档是一个有序的、带标签的树。树的每个节点都是一个 XML 元素,并写有一个开始和结束标记。

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

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

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

您被告知哪些元素已“启动” 你被告知哪些元素结束

举个例子:

<outer>
  <inner/>
</outer>

这将导致这样的一系列事件/回调:

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

因此,“显然”,当您的解析器记住事件历史时,确定innerouter 中的哪一个是叶节点是直截了当的!

因此,答案是:不,您不需要 DOM 解析器。最后,无论如何,DOM 都是由相同的信息构成的!如果 DOM 解析器可以推断出对象的“范围”,那么您的 SAX 解析器也可以。

但只是为了记录:您仍然需要仔细实现跟踪“开始”、“打开”和“结束”标签的数据结构,例如正确确定这个:

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

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

【讨论】:

@tobias_k 感谢您的意见,我希望我的更新答案能反映您的观点。【参考方案2】:

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

每当应用 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>

【讨论】:

以上是关于使用 SAX 解析器确定是不是在叶节点的主要内容,如果未能解决你的问题,请参考以下文章

如何让 SAX 解析器从 xml 声明中确定编码?

解析XML文件之使用SAM解析器

在 JAVA 中使用 SAX 解析器从 XML 文件中提取文本节点

如何在 android 中使用 DOM 或 SAX 解析器从 XML 读取子节点

XMLReader 是 SAX 解析器、DOM 解析器,还是两者都不是?

在 Java 中使用 SAX 解析器的堆栈溢出错误