你如何欺骗SAX在Java中将大块分成更小的块?

时间:2015-04-30 16:52:18

标签: java xml sax

我凌驾于

public void characters(char[] ch,
                       int start,
                       int length)
                throws SAXException

解析XML文档时。它适用于小型XML文档(1MB或更少)。但是,如果XML文档是100MB或更多)我开始在此函数内获取java堆空间内存错误。

在做了一些研究之后here我意识到上面的字符数组“ch”的大小取决于“startElement()”和“endElement()”之间的字符数。

所以我想知道,是否有一种智能方法可以覆盖“startElement”和“endElement”以返回较小的数据块?

例如,假设我有一个XML文件,如下所示:

<element1>
   This is a very simple sentence that is not very long. 
</element1>

目前“characters()”函数将返回“这是一个非常简单的句子,不会很长。”作为一个块。

但我的问题是如果XML文件如下:

<element1>
   An entire book's worth of content is pasted in this single element. Possibly millions of characters.  
</element1>

有没有办法可以“欺骗”startElement()和endElement()让characters()函数返回整本书的书籍内容?

2 个答案:

答案 0 :(得分:1)

您无法欺骗 SAX解析器,以较小的块为您的ContentHandler提供字符数据。解析器已被允许以小于元素的整个内容的块的形式提供它,然而,当内容很大时,我希望这是它们的正常行为。否则将要求他们在内部缓冲潜在的大量数据,无特殊原因。

如果您发现您的特定解析器确实提供了比您想要的更大的块,那么您应该查阅其文档。您可以设置一个选项来影响它。否则,您的另一种选择是找到或制作一个您更喜欢其行为的解析器。

但是,请检查一下你的事实。可能是解析器确实以合理大小的块返回数据,而问题实际上就在于您正在使用它们。特别是,如果要构建整个文件的某种内存表示形式,那么接收它的大小几乎不重要。

还要考虑增加VM的最大堆大小。一个100MB的文件并不是非常庞大,但是你的虚拟机可能会受到堆大小限制的影响,而这个限制对于这个问题来说是不切实际的低。

答案 1 :(得分:0)

非常老的线程,但是最近我不得不处理类似的任务,因此发布我的答案对其他一些人可能很有用。我认为后期Java版本jdk 9具有读取字符数据块的功能,您只需要设置CDATA_CHUNK_SIZE,即可在Stax和Sax解析器中使用。

import java.io.FileInputStream;
import java.io.InputStream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

public class SaxDemo {

    final static String CDATA_CHUNK_SIZE = "jdk.xml.cdataChunkSize";

    public static void main(String argv[]) {

        try {

            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();

            XMLReader reader = sp.getXMLReader();
            reader.setContentHandler(new TestHandler());

            try (InputStream is = new FileInputStream("FileDir/file")) {

                reader.setProperty(CDATA_CHUNK_SIZE, 10000);

                reader.parse(new InputSource(is));
                System.out.println("Done");

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}