最近在项目中需要把一些系统配置表导到XML文件中,一开始觉得数据量不大,使用dom4j就可以了,后来才发现数据量超出预期的大小,程序很快就内存溢出了。SAX在顺序遍历XML所有节点时,还是有很大的优势的。下面是我写的SAX解析XML的代码,解析大型XML文件来说性能比较好。
DefaultElementHandler.java
import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public abstract class DefaultElementHandler extends DefaultHandler{ //入栈标志 private boolean begin; //节点名称 private String tagName; //内容缓冲区 private StringBuilder sb; //带节点名称的构造方法 public DefaultElementHandler(String tagName) { this.tagName = tagName; this.begin = false; this.sb = new StringBuilder(); } /** * 处理节点开始 */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals(tagName) || begin) { sb.append("<"); sb.append(qName); sb.append(" "); int attrCount = attributes.getLength(); for(int i=0;i<attrCount;i++){ sb.append(attributes.getQName(i)); sb.append("=\""); sb.append(attributes.getValue(i)); sb.append("\" "); } sb.append(">"); begin = true; } } /** * 处理节点内容 */ public void characters(char[] ch, int start, int length) throws SAXException { String text = new String(ch, start, length); if (text.trim().equals("")) return; if (begin) sb.append(text); } /** * 处理节点结束 */ public void endElement(String uri, String localName, String qName) throws SAXException { String stag = "</" + tagName + ">"; String ntag = "</" + qName + ">"; if (stag.equals(ntag) || begin) { sb.append(ntag); if (stag.equals(ntag)) { begin = false; try { Document doc = DocumentHelper.parseText(sb.toString()); Element element = doc.getRootElement(); this.processElement(element); } catch (DocumentException e) { e.printStackTrace(); } sb.setLength(0); } } } /** * 对节点进行操作 * @param element */ public abstract void processElement(Element element); }
SAXReadXML
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.dom4j.Element; public class SAXReadXML { /** * @param args * @throws Exception * @throws */ public static void main(String[] args) throws Exception { SAXParserFactory sf = SAXParserFactory.newInstance(); SAXParser sax = sf.newSAXParser(); //"TABLE"是需要处理的节点 sax.parse("d:\\myxml.xml", new DefaultElementHandler("TABLE"){ @Override public void processElement(Element element) { //这里获得了每一个"TABLE"节点,在这里做具体的操作。 System.out.println(element.asXML()); } }); } }
用这段程序解析1GB的XML文件都不会出现内存溢出,而且同时拥有dom4j对节点处理的方便性。
myxml.xml
<?xml version="1.0" encoding="UTF-8"?> <TABLES> <TABLE> <ID>ID0</ID> <NAME>NAME0</NAME> <VALUE>VALUE0</VALUE> <DESC>DESC0</DESC> <COLUMNS> <COLUMN> <C1>C10</C1> <C2>C20</C2> <C3>C30</C3> </COLUMN> <COLUMN> <C1>C10</C1> <C2>C20</C2> <C3>C30</C3> </COLUMN> </COLUMNS> </TABLE> <TABLE> <ID>ID1</ID> <NAME>NAME1</NAME> <VALUE>VALUE1</VALUE> <DESC>DESC1</DESC> <COLUMNS> <COLUMN> <C1>C11</C1> <C2>C21</C2> <C3>C31</C3> </COLUMN> <COLUMN> <C1>C11</C1> <C2>C21</C2> <C3>C31</C3> </COLUMN> </COLUMNS> </TABLE> <TABLE> <ID>ID2</ID> <NAME>NAME2</NAME> <VALUE>VALUE2</VALUE> <DESC>DESC2</DESC> <COLUMNS> <COLUMN> <C1>C12</C1> <C2>C22</C2> <C3>C32</C3> </COLUMN> <COLUMN> <C1>C12</C1> <C2>C22</C2> <C3>C32</C3> </COLUMN> </COLUMNS> </TABLE> </TABLES>