大型XML文件的处理方法(SAX方式)

大型XML文件的处理方法

在实际工作中,我们经常需要处理各种大型的XML文件,通常在10M以上,且一般规则比较简单,但是对内存和时间的要求都比较高。在这种情况下,不能使用DOM方式,因为DOM方式必须在解析文件之前把整个文档装入内存,在处理大型文件时其性能下降的非常厉害,通常会导致OutOfMemory异常。
 本文推荐使用的是Simple API for XML(SAX)方式,在SAX解析器装载XML文件时,它遍历文件文档并在其主机应用程序中产生事件(经由回调函数、指派函数或者任何可调用平台完成这一功能)表示这一过程。在解析同等大小的文档时SAX通常会相比DOM提供更好的性能(因为DOM必须遍历树结构)。此外,与DOM相比,因为在给定的时间之内只需要XML文档的一部分装入内存,所以SAX 通常在处理更大文件时内存的利用效率也来得更高,SAX应用程序的事件处理结构一般意味着SAX应用程序是针对特定文件结构定制构建的,而DOM应用程序则更具一般性。

 下面用实例来说明,有一文件(http://zhouxianli.blogdriver.com/zhouxianli/inc/xmlfile.xml),其中的item和area都是多条的,通常每次处理时的文件大小在10M以上,使用DOM方式开发时直接OutOfMemory。如是按照SAX方式编写如下代码:

 

package test.xml;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SaxTest {
 public static void main(String args[]) throws Exception {
  System.out.println("UseMemory="
    + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
      .freeMemory()));

  SAXParserFactory factory = SAXParserFactory.newInstance();
  SAXParser saxParser = factory.newSAXParser();
  ItemProcessHandler handler = new ItemProcessHandler();
  System.out.println("UseMemory="
    + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
      .freeMemory()));
//   saxParser.parse("src/main/java/test/xml/xmlfile.xml", handler);
  saxParser.parse("src/main/java/test/xml/bigdata.xml", handler);

  System.out.println("UseMemory="
    + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
      .freeMemory()));
 }
}

class ItemProcessHandler extends DefaultHandler {
 // 用于存放当前元素的值
 private StringBuilder currentValue = new StringBuilder();

 // Item中的各个属性
 private String areaid;
 private String key;
 private String intervaltype;
 private String value;
 private String remark;

 public void startElement(String uri, String localName, String qName,
   Attributes attributes) throws SAXException {
  // 按item遍历的,所以在此初始化。因为areaid不在item块中,所以不清除
  if (qName.equals("item")) {
   key = null;
   intervaltype = null;
   value = null;
   remark = null;
  }
  // 清空当前元素值
  currentValue.setLength(0);
 }

 public void endElement(String uri, String localName, String qName)
   throws SAXException {
  // 对每个元素获取其值
  if (qName.equals("areaid")) {
   areaid = currentValue.toString();
  } else if (qName.equals("key")) {
   key = currentValue.toString();
  } else if (qName.equals("intervaltype")) {
   intervaltype = currentValue.toString();
  } else if (qName.equals("value")) {
   value = currentValue.toString();
  } else if (qName.equals("remark")) {
   remark = currentValue.toString();
  } else if (qName.equals("item")) {
   // item元素结束时,处理Item
   processItem(areaid, key, intervaltype, value, remark);
  }
 }

 public void characters(char ch[], int start, int length)
   throws SAXException {
  currentValue.append(ch, start, length);
 }

 // 处理Item的方法体,注意处理异常
 public void processItem(String areaid, String key, String intervaltype,
   String value, String remark) {
  try {
//   System.out.println("areaid=" + areaid + ",key=" + key
//     + ",intervaltype=" + intervaltype + ",value=" + value
//     + ",remark=" + remark);
  } catch (Exception e) {
   throw new IllegalStateException(e);
  }
 }
}

注:上述是我建议的处理方式,将属性赋值放在endElement中处理,将实际业务操作放在processXXX中处理,需要记住的是,不要采取数据解析和业务处理分离的方式(如:让handler返回一个List,得到List后循环处理数据的方式),因为那样会带来大量额外的内存消耗(List的数据所需内存)。

首先,使用xmlfile.xml(700字节)测试,打印
UseMemory=277952
UseMemory=497544
UseMemory=636760

然后,使用bigdata.xml(11,378,841字节)测试,打印
UseMemory=277952
UseMemory=497544
UseMemory=792512

对比两个结果,可以发现处理bigdata.xml这么大的数据时,实际消耗的内存也是非常小的,适合于处理大型XML文件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值