Java SAX解析器,存储属性

3

我正在尝试将当前文档位置存储在堆栈中,在startElement时进行推送,在endElement时进行弹出。目前我正在使用以下代码:

public void startElement(String namespaceURI, String elname,
                         String qName, Attributes atts) throws SAXException {
    original.append(innerText);
    original.append("<");
    original.append(elname);
    original.append(">");
    docStack.push(new StackElement(elname,atts));
....

很不幸,当它尝试在后面读取atts时,会出现错误: Caused by: java.lang.IllegalStateException: Attributes can only be used within the scope of startElement()。

有没有快速可靠的方法来存储这些属性? 此外,是否有比为每个开始标记构建新的自定义对象StackElement更好的方法?

2个回答

4
当你将属性推入自定义对象栈时,你正在取实际的属性对象,根据文档所说,它是这样的:
atts - 附加到元素的属性。如果没有属性,则为空Attributes对象。在startElement返回后该对象的值未定义(强调我的)
你应该在你的startElement(...)方法中使用Map<String,String>捕获属性。这样你就可以在任何你想要使用的地方使用它们了。

看起来每次调用startElement都构造对象会有点慢,但即使有这个额外开销,它仍然比DOM高效得多,对吗? - NoBugs
1
它肯定比DOM更高效,因为你不会存储所有东西,只存储一些东西。我认为你对对象创建的担忧是没有根据的,因为Java不是一种缓慢而笨重的语言。在解析完成后,内存和对象将被清理。 - nicholas.hauschild
这个问题真的很可怕。我有一个500M大小的文档,在处理十多分钟后,它在中途失败了。我只是为了在endElement函数中使用而缓存了属性对象(因为我的模型对象需要元素文本和属性)。结果发现它有效... 一段时间... 看来阅读细节很重要。 - Lucas
1
你可以使用 org.xml.sax.helpers.AttributesImpl 替代 Map,它有一个构造函数可以接受一个 Attributes 对象。这样做的开销可能是相似的。 - Jörn Horstmann

4
如果Attributes是上下文敏感的,则在StackElement构造函数中从中提取所需内容(并且不要存储引用)。
可以这样做:
public class StackElement {

    private Map<String, String> map = new HashMap<String, String>();

    public StackElement(String elname, Attributes atts) {

        for (int i = 0; i <  atts.getLength(); i++) {
            map.put(atts.getQName(i),atts.getValue(i));
        }
    }
}

p.s. 看起来我抄袭了@nicholas的答案,但是老实说,我已经打出来并且正在编写代码时他发布了他的答案。


我曾考虑过将所有内容复制到类似的映射中,但我认为这可能是低效的,特别是因为我不需要每个元素上的所有属性。这样做会更慢,因为它要构建两个新类、构建一个新的StackElement和HashMap,并查询每个元素的所有属性!我的问题是,有没有更有效的方法来做到这一点,也许是“冻结”属性并添加到StackElement中? - NoBugs
getQName应该改为getLocalName。 - NoBugs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接