在JAVA中解析大型XML文档

12
我有以下问题:
我有一个XML文件(约1GB),必须上下迭代(即非顺序的,一个接一个)以获取所需数据并对其进行一些操作。最初,我使用DOM Java包,但显然,在解析XML文件时,JVM达到其最大堆空间并停止。
为了解决这个问题,我想出的一个解决方案是找到另一个解析器,遍历XML中的每个元素,然后将其内容存储在我的硬盘上的临时SQLite数据库中。因此,以这种方式,JVM的堆不会超过,并且一旦填充所有数据,我就忽略XML文件并继续在临时SQLite数据库上进行操作。
我还有其他方法可以解决手头的问题吗?

1
使用JAXB解析XML。 - Biswajit
1
正如其他人所说,你需要使用SAX解析器而不是DOM解析器,它将完全满足你的需求。阅读这篇文章:https://dev59.com/A2w15IYBdhLWcg3wGn9c - cowls
如果你不能一次性加载整个DOM树,你必须找到一种按顺序进行处理的方法。这可能吗?你能展示一个满足你需求的XSLT吗? - Thorbjørn Ravn Andersen
1
解析大型XML文件时,始终使用SAX解析器。请参考以下链接StackOverflow - Yogesh Kulkarni
@GaborSch ... 我已经尝试增加Java堆空间,但仍然出现相同的异常。此外,由于大小可能会进一步增加,我宁愿选择一个不受此限制的解决方案。例如,非连续的情况下,我可能需要从第2个元素中获取数据,而在第5个元素中获取数据。是的,正如您指出的那样,我的XML中有不同的数据,并且需要进行交叉引用。我认为,正如其他人所指出的那样,最好使用SAX解析器,它将仅将当前元素标记存储在内存中(而不是整个XML结构)。 - cgval
显示剩余2条评论
4个回答

13

SAX(Simple API for XML)可以帮助您。

与DOM解析器不同,SAX解析器不会创建XML文档的内存表示,因此速度更快,使用的内存更少。相反,SAX解析器通过调用回调函数即调用提供给解析器的org.xml.sax.helpers.DefaultHandler实例上的方法来通知客户端XML文档的结构。

这是一个示例实现:

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
DefaultHandler handler = new MyHandler();
parser.parse("file.xml", handler);

MyHandler中定义了当生成文档/元素的开始/结束事件时要执行的操作。
class MyHandler extends DefaultHandler {

    @Override
    public void startDocument() throws SAXException {
    }

    @Override
    public void endDocument() throws SAXException {
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
    }

    // To take specific actions for each chunk of character data (such as
    // adding the data to a node or buffer, or printing it to a file).
    @Override
    public void characters(char ch[], int start, int length)
            throws SAXException {
    }

}

2
如果你曾经做过 SAX 解析,你可能知道 characters() 方法也非常重要,你必须对字符数据进行缓冲,因为不能保证内容数据在一个块中处理(也就是说,可能会立即执行两次 character() 调用)。我认为值得一提。 - gaborsch
2
我并不是想让我的解决方案完美无缺。这只是一个初步的实现。谢谢你指出来。我会更新我的答案。 - user2030471

3
如果您不想受到内存限制的束缚,我强烈建议您使用当前的方法,并将所有内容存储在数据库中。
XML文件的解析应该由SAX解析器完成,就像每个人(包括我)都推荐的那样。这样,您可以逐个创建对象,并立即将其持久化到数据库中。
对于后处理(解决交叉引用),可以使用数据库中的SELECT、主键、索引等。如果您感觉舒适,还可以使用ORM(Eclipselink、Hibernate)。
实际上,我并不真正推荐SQLite,最好是使用MySQL服务器进行设置并将数据存储在那里。稍后,您甚至可以重复使用XML数据(如果不删除)。

我想知道有人如何相信搭建一个完整的数据库服务器比使用嵌入式数据库更容易,而在使用嵌入式数据库时,你只需要包含一个JAR文件,而不需要安装任何东西。我认为对于这种情况来说,单独使用数据库服务器就过度了。也许还有其他一些好的原因可以使用数据库服务器,但是更容易设置吗?真的吗? - vanje
@vanje 我不是指Oracle :) 我们在谈论MySQL。说真的,我无法相信对于任何开发人员来说设置MySQL服务器会成为一个问题。 - gaborsch
我认为每个开发者都应该能够执行Oracle和MySQL的基本安装。我同意你的看法,即Oracle比MySQL复杂得多。但这不是重点。你将MySQL与SQLite进行了比较,并表示MySQL更容易设置。但你没有提到在你看来什么方面更容易。 - vanje

1
如果您想使用比SAX更高级的方法,这可能会非常棘手,您可以查看使用最近的Saxon-EE版本进行流式XSLT转换。但是,您对于您正在进行的精确处理过程过于模糊,无法确定此方法是否适用于您的特定情况。

0
如果您需要一种资源友好的方法来处理非常大的XML,请尝试这个: http://www.xml2java.net/xml-to-java-data-binding-for-big-data/ 它允许您以SAX方式处理数据,但具有获取高级事件(将XML数据映射到Java)并能够直接在代码中使用这些对象的优势。因此,它结合了JAXB的便利性和SAX的资源友好性。

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