解析Java中的大型XML响应

问题描述:

我有一个Java程序向一个我无法修改的Web服务发出请求。如果我尝试将其解析为Document对象,则其中一个请求的响应可能非常大,直至堆内存不足。为了解决这个问题,我正在将响应逐块读取到byte []缓冲区中,并将其写入磁盘。然后我就在扫描文件中的行由行和建筑Document对象了,我觉得每一个元素(这些都是我需要出响应的唯一元素)计划:解析Java中的大型XML响应

StringBuilder sb = null; 
String line = null; 

while((line = reader.readLine()) != null){ 
    if(line.trim().equals("<bond>")){ 
     sb = new StringBuilder(line); 
    } 
    else if(line.trim().equals("</bond>")){ 
     Document doc = builder.parse(sb.toString()); 
     // Process doc 
    } 
    else{ 
     sb.append(line); 
    } 
} 

不幸的是,似乎换行符被转换为响应中的空格,所以一切都是一条巨大的线。我正在考虑的一种解决方案是使用SAX来处理解析,并以相同的方式构建我的Document部分。有没有人有另一种解决方案,或者这是我最好的选择?

感谢, 贾里德

+0

Java的跨站脚本是后容易3MB大多数机器上。要用完堆,你的回应应该大大超过3mb,这是巨大的。但是,如果您的响应仅比3mb略大,并且您有时仅进入java.lang.*Error,则可能需要将xss选项略微增加至4mb。例如,在我的CentOS中,我将它设置为5mb,因为我用递归做了很多事情。但是,如果你确实坚持从Document对象读取它,那么SAX解析器是理智的方法。请记住,就CPU周期而言,这将是双重工作。 – 2011-06-14 13:44:03

如果您想使用SAX或DOM解析器,SAX解析器可能是您最好的选择。它不会将xml存储在内存中,因此它将能够处理更大的XML文件。

你可以看看作为Nux这样的库这将使您能够结合XML使用XPath流中提取你想要的值。这可能是值得一看的,而不是试图写一些自定义的东西。

如果响应非常大,是的,SAX解析器将是合适的,否则在创建DOM结构时会再次耗尽内存。

我也可以推荐Smooks框架将XML转换为其他形式。它非常适合处理非常大的数据集,并且有很多预先构建的内容(http://www.smooks.org)。 Smooks允许您指定用于生成新的Java对象,XML或其他内容的XML结构的哪些部分。

+0

用户指南:http://www.smooks.org/mediawiki/index.php?title=V1.4:Smooks_v1.4_User_Guide 示例:http://www.smooks.org/mediawiki/index.php?title= Smooks_v1.4_Examples – 2011-06-14 13:47:47

在Java中解析XML文档有不同的API。有DOM API,你似乎正在使用。它读取整个XML文档并将其转换为节点树;你会得到包含所有这些节点的Document对象。 DOM API的优点在于使用起来相当简单,但缺点是如果XML很大,所有这些节点都会占用大量内存,正如您已经注意到的那样。

还有SAX API,其工作方式不同。这可以通过回调机制来实现:告诉XML解析器,只要遇到XML文件中的开始或结束标记或数据,就要调用它。然后,您可以使用回调方法决定要执行的操作,并且只存储所需的数据。其优点是可以扩展到大型文档,因为整个XML树不需要驻留在内存中。缺点是这个API的层次较低,使用起来更麻烦。

也有StAX它被设计成DOM和SAX API之间的东西。

如果您需要处理大型XML文档,那么最好使用SAX或StAX API而不是DOM API。

如果堆的大小是一个问题,你可以尝试用以下选项增加它:

的java -Xms64m -Xmx256m

这会给你64MB的起始堆大小和最大256MB。您可以使用其他值。这具有不需要任何代码改变的优点。

我认为使用SAXBuilder和XPath可能比while循环更好。
东西就这些线 -

Document doc = new SAXBuilder().build(new StringReader(xmlStr)); 
XPath xPath = XPath.newInstance("/*/YourElement"); 
Element ele = xPath.selectSingleNode(doc); 
ele.getChild("ChildElement");