1

我需要处理一个巨大的 XML 文件,4G。我使用 dom4j SAX,但编写了自己的 DefaultElementHandler。代码框架如下:

SAXParserFactory sf = SAXParserFactory.newInstance();   
SAXParser sax = sf.newSAXParser();   
sax.parse("english.xml", new DefaultElementHandler("page"){   
public void processElement(Element element) { 
// process the element
}
});    

我以为我正在按“页面”处理巨大的文件“页面”。但似乎不是,因为我总是遇到内存不足错误。我错过了什么重要的事情吗?谢谢。我是 XML 流程的新手。

4

3 回答 3

0

好吧,您并没有真正按页面处理 XML,但是如果您扩展 XMLFilterImpl 而不是使用 DefaultElementHandler(无论是什么),那么您可以简单地处理 XML 元素。您将进行流式传输,因此不会有整个文档在内存中的情况(实际上)。

本质上,您将在元素的开头、属性、内部的文本以及元素的末尾(查看 ContentHandler 接口中的方法)调用事件元素。根据这些调用,您可以进行处理(您可能需要一些数据结构,在其中累积“页面”元素内的元素。另请注意,不能保证您只会收到一次文本调用(它是由解析器决定)。

这有助于使其更清晰吗?

于 2009-12-16T10:05:34.173 回答
0

我认为它只读取元素内的所有内容,因为我只是在网上按照一个例子......

公共抽象类 DefaultElementHandler 扩展 DefaultHandler{ 私有布尔开始;私有字符串标签名;私有 StringBuilder sBuilder;

public DefaultElementHandler(String tagName) {
    this.tagName = tagName;
    this.begin = false;
    this.sBuilder = new StringBuilder();
}

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    if (qName.equals(tagName)||begin){
        sBuilder.append("<");
        sBuilder.append(qName);
        sBuilder.append(" ");
        int attrCount = attributes.getLength();
        for (int i=0; i<attrCount; i++) {
            sBuilder.append(attributes.getQName(i));
            sBuilder.append("=\"");
            sBuilder.append(attributes.getValue(i));
            sBuilder.append("\" ");
        }
        sBuilder.append(">");
        begin = true;
    }
}

public void characters(char[] ch, int start, int length) throws SAXException{       
    StringBuilder sb = new StringBuilder();
    for(int i=0; i < length; i++) {
        sb.append(convertSpecialChar(ch[start+i]));
    }

    String text = sb.toString().trim();      
    //String text = new String(convertSpecialChar(ch), start, length);
    if (text.trim().equals("")) return;
    if (begin) sBuilder.append(text);
}

public void endElement(String uri, String localName, String qName) throws SAXException {
    String stag = "</" + tagName + ">";   
    String ntag = "</" + qName + ">";   
    if (stag.equals(ntag) || begin) {   
        sBuilder.append(ntag);   
        if (stag.equals(ntag)) {   
            begin = false;   
            try {   
                Document doc = DocumentHelper.parseText(sBuilder.toString());   
                Element element = doc.getRootElement();   
                this.processElement(element);   
            } catch (DocumentException e) {   
                e.printStackTrace();  
                System.exit(1);
            }   
            sBuilder.setLength(0);   
        }   
    }   
}
于 2009-12-16T10:49:07.740 回答
0

您的 DefaultElement 实现对我来说看起来很困惑。看起来一切都堆积在 sBuilder 中,并且在找到根元素的末尾或更可能耗尽内存之前,它永远不会被清除。

如何读入元素文本取决于你需要解析什么样的xml。每个元素都可以有文本,并且可以穿插子元素。通常,您会在 Web 服务和配置文件中看到那种 xml,其中所有元素文本都在叶元素中,然后在某些情况下,例如 XHTML,就会发生穿插的事情。

如果您的 xml 架构的工作方式是所有文本信息都在叶子元素中,那么您可以缓冲从 startElement 开始的文本,并使用 endElement 中累积的文本,然后清除缓冲区。

这是一篇关于 SAX 的好文章:http ://www.javaworld.com/javaworld/jw-08-2000/jw-0804-sax.html

于 2009-12-16T13:56:42.170 回答