使用 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",当你的解析器记住事件的历史,确定inner
或outer
中的哪个是叶节点直截了当!
因此,答案是:不,您不需要 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>
使用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",当你的解析器记住事件的历史,确定inner
或outer
中的哪个是叶节点直截了当!
因此,答案是:不,您不需要 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>