`

dom4j处理大文件

    博客分类:
  • xml
阅读更多

原文 http://blog.csdn.net/shuhang1106/archive/2008/07/09/2627581.aspx

 

前几天因为个人爱好,学习在Java中处理XML文档。java是开源的,自然用于处理xml的技术也是满天星斗,在网上奔波了数十分钟最终决定使用 dom4j。对于入门的使用就像是羊粑粑沥漏落落满天星,很快掌握。可是当读写的xml文档稍大时这些代码就经受不住考验了,一个老问题内存溢出。这说明 网上大部分的代码是将xml文档全部读入内存进行操作或是在内存中建立完整的xml文档再一次性的写入文件。有办法了,求助于网络,百度、 google搜了便,可结果是城市的天空根本看不见星星。更有为数不少的人得出结论,使用dom4j操作xml文档时受到文件大小的限制(纯属谬论),那 么多的高手开发出的dom4j会考虑不到大文件的问题?求人不如求己,虽然本人英语其差(大学英语4级290分),还是决定上官方网站寻求答案(www.dom4j.org )。刚登录就在常见问题中看到了下面的问题

How does dom4j handle very large XML documents?(该句中80%的单词我还是认识的)
下面是本人的理解英语好的人应该去看英文原版,不要看我的垃圾翻译。
­
dom4j提供了基于事件的模型来操作xml文档。利用该模型开发人员可以一部分、一部分的处理XML文档,而不需要将整个XML文档都加载到内存中。例如:假想你要处理一个非常大的XML文档,它可能是由数据库的某张数据表而来的。如下所示:
<ROWSET>
<ROW ID="1">
  ...
</ROW>
<ROW ID="2">
  ...
</ROW>
...
<ROW ID="N">
  ...
</ROW>
</ROWSET>
我们可以在某一时间只处理一个ROW节点,而不必立刻将文档的所有内容加载到内存中。dom4j提供一个基于事件的模型来实现它。我们可以注册 一个事件处理器来处理一个或多个路径表达式。事件处理器会在注册路径的开始和结束时被调用执行。当注册路径的开始标签找到时执行事件处理器的 onStart()方法,当注册路径的结束标签被找到时执行事件处理器的onEnd()方法。
­
onStart()和onEnd()方法传递一个ElementPath实例参数,这个实例既为根据注册路径遍历xml文档时的当前节点(Element)。如果想对遍历的当前节点进行操作,可以在onEnd()方法中对当前节点调用detach()方法保存改。
下面是示例代码:
 

SAXReader reader = new SAXReader();
reader.addHandler( "/ROWSET/ROW",
    new ElementHandler() {
        public void onStart(ElementPath path) {
            // do nothing here...    
        }
        public void onEnd(ElementPath path) {
            // process a ROW element
            Element row = path.getCurrent();
            Element rowSet = row.getParent();
            Document document = row.getDocument();
            ...
            // prune the tree
            row.detach();
        }
    }
);

 
Document document = reader.read(url);
上面的办法解决了读的问题可是写的问题还没有解决。我命由我不由天(吹牛皮),畅游dom4j的doc文档找到如下几个方法
startDocument()
writeOpen();
writeClose();
endDocument()
动手一试,问题搞定,看来牛皮没白吹。下面是示例代码:
 

/**
  * 数据写入xml文件
  * @param filePath 目标xml文件的存放路径
  * @return
  */
public boolean writeXML(String filePath){
  XMLWriter out;
  try {
  
   /*
    * 创建XMLWriter对象,设置XML编码,解决中文问题。
    */
   OutputFormat outputFormat = OutputFormat.createPrettyPrint();
   outputFormat.setEncoding("GBK");
   out = new XMLWriter(new FileWriter(filePath),outputFormat);
  
  
   out.startDocument();
   Element rootElement = DocumentHelper.createElement("mans");
   out.writeOpen(rootElement);
  
   /*
    * 向mans节点写入子节点
    */
   for(int i=0 ; i<1000000 ; i++){
    Element man = createManElement(new Long(i), "shuhang"+i);//用于创建节点的方法
    out.write(man);
    System.out.println(" the loop index is : " + i);
   }
  
   out.writeClose(rootElement);
   out.endDocument();
  
   out.close();
   return true;
  } catch (IOException e) {
   e.printStackTrace();
   return false;
  } catch (SAXException e) {
   e.printStackTrace();
   return false;
  }
}

 
­
新问题出现。对于读取数据的方法,在onEnd()方法中只是进行对Element的简单操作而已,若要对Element进行复杂的处理怎么 办,如将Element节点的数据写入数据库,无法在onEnd()方法中加入过多的代码,因为无法通过编译。仔细想了一下dom4j的注册事件处理器应 该用的是模板方法,通过钩子将用户的操作加入到模板中去。何不自己写一个事件处理器来完成我们自己的定制操作,代码如下:
 
 

public class ManElementHandler implements ElementHandler {
public String mdbName;
­
public ManElementHandler(){
  
}
­
public ManElementHandler(String mdbName){
  this.mdbName = mdbName;
}
­
public boolean saveMan(Element element, String mdbName){
  return true;
}
­
public void onEnd(ElementPath arg0) {
         Element row = arg0.getCurrent();
         Element rowSet = row.getParent();
         Document document = row.getDocument();
         Element root = document.getRootElement();
  Iterator it = root.elementIterator();
  while(it.hasNext()){
   Element element = (Element)it.next();
   System.out.println(" id : " + element.elementText("id") + " name : " + element.elementText("name"));
   saveMan(element, this.mdbName);
  }
         row.detach();
}
public void onStart(ElementPath path) {
  
}
}

 
下面给出完整的实例代码。该实例首先创建一个xml文档,然后读取xml文档中的数据并将数据写入Access数据库中。测试时使用的文件大小为125M未发生内存溢出。
 

//*****************************************************************************************************************
package com;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* 获取Access数据库的连接
* @author 佛山无影脚
* @version 1.0
* Jul 7, 2008  4:35:49 PM
*/
public class AccessMDBUtil {
public static Connection connectMdb(String mdbName) {
  if(mdbName == null || mdbName.equals("")){
   return null;
  }
  try {
   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
   String dburl ="jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ="+mdbName;
   Connection conn=DriverManager.getConnection(dburl);
   return conn;
  } catch (Exception e) {
   e.printStackTrace();
   return null;
  }
}
}
//*******************************************************************************************************************
//*******************************************************************************************************************
package com;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.ElementHandler;
import org.dom4j.ElementPath;
/**
* 定制的事件处理器
* @author 佛山无影脚
* @version 1.0
* Jul 7, 2008  4:35:49 PM
*/
public class ManElementHandler implements ElementHandler {
public String mdbName;//数据库名称 含路径
­
public ManElementHandler(){
  
}
­
public ManElementHandler(String mdbName){
  this.mdbName = mdbName;
}
­
/**
  *将Element节点数据保存到数据库
  *@param element 遍历XML文档时的当前节点
  *@param mdbName 数据库名称 含路径
  */
public boolean saveMan(Element element, String mdbName){
  System.out.println(" the method saveMan in ManElementHandler is execute!");
  Statement stmt = null;
  ResultSet rs = null;
  Connection conn = null;
  try {
   conn= AccessMDBUtil.connectMdb(mdbName);
   stmt=conn.createStatement();
   String sql = "insert into mans(id,name) values(" + element.elementText("id") + ",'" + element.elementText("name") + "')";
   stmt.executeUpdate(sql);
  
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  } finally {
   if(rs != null) {
    try {
     rs.close();
    } catch (SQLException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   if(stmt != null) {
    try {
     stmt.close();
    } catch (SQLException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   if(conn != null) {
    try {
     conn.close();
    } catch (SQLException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  
  }
  
  return true;
}
­
public void onEnd(ElementPath arg0) {
         Element row = arg0.getCurrent();
         Element rowSet = row.getParent();
         Document document = row.getDocument();
         Element root = document.getRootElement();
  Iterator it = root.elementIterator();
  while(it.hasNext()){
   Element element = (Element)it.next();
   System.out.println(" id : " + element.elementText("id") + " name : " + element.elementText("name"));
   saveMan(element, this.mdbName);
  }
          row.detach();
}
public void onStart(ElementPath path) {
  
}
}
//*************************************************************************************************************************
//*************************************************************************************************************************
package com;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* 测试
* @author 佛山无影脚
* @version 1.0
* Jul 7, 2008  4:35:49 PM
*/
public class ManTest {
/**
  * 创建man节点
  * <man><id>1</id><name>佛山无影脚</name></man>
  * @param id
  * @param name
  * @return
  */
public Element createManElement(Long id,String name){
  Element manElement = DocumentHelper.createElement("man");
  manElement.addElement("id").addText(id.toString());
  manElement.addElement("name").addText(name);
  return manElement;
}
­
/**
  * 数据写入xml文件
  * @param filePath 目标xml文件的存放路径
  * @return
  */
public boolean writeXML(String filePath){
  XMLWriter out;
  try {
  
   /*
    * 创建XMLWriter对象,设置XML编码,解决中文问题。
    */
   OutputFormat outputFormat = OutputFormat.createPrettyPrint();
   outputFormat.setEncoding("GBK");
   out = new XMLWriter(new FileWriter(filePath),outputFormat);
  
  
   out.startDocument();
   Element rootElement = DocumentHelper.createElement("mans");
   out.writeOpen(rootElement);
  
   /*
    * 向mans节点写入子节点
    */
   for(int i=0 ; i<1000000 ; i++){
    Element man = this.createManElement(new Long(i), "shuhang"+i);
    out.write(man);
    System.out.println(" the loop index is : " + i);
   }
  
   out.writeClose(rootElement);
   out.endDocument();
  
   out.close();
   return true;
  } catch (IOException e) {
   e.printStackTrace();
   return false;
  } catch (SAXException e) {
   e.printStackTrace();
   return false;
  }
}
­
/**
  * 从xml文件中读取数据,并将数据写入Access数据
  * @param filePath xml文件的存放路径
  * @return
  */
public boolean readXML(String filePath){
  
  ManElementHandler manElementHandler = new ManElementHandler("F:\\dom4j\\xmlTest.mdb");
  SAXReader reader = new SAXReader();
  reader.addHandler( "/mans/man", manElementHandler);
  Document document = null;
     try {
      File file = new File(filePath);
   document = reader.read(file);
  } catch (DocumentException e) {
   e.printStackTrace();
   return false;
  }
  return true;
}
­
­
public static void main(String[] args){
  XMLReader reader = null;
  long startTime = System.currentTimeMillis();
  ManTest mantest = new ManTest();
  
  mantest.writeXML("f:\\dom4j\\mans.xml");
  mantest.readXML("f:\\dom4j\\mans.xml");
  
  long endTime = System.currentTimeMillis();
  System.out.println(" is end! the millis is : " + (endTime - startTime));
  
}
}
//*************************************************************************************************************************
­

 
 

 

 

分享到:
评论
1 楼 liwanfeng 2012-08-14  
情况是这样的,你的文件我觉得还是比较小,我现在需要处理的XML文件有近600M,直接用SAXReader将文件读入到系统中,在不做任何操作的前提下,内存就已经1.5G了,内存真的吃不消啊!怎么办呢?

相关推荐

    dom4j处理xml文档说明

    dom4j处理xml文档,详细说明,内容全面,详细代码示例

    DOM4j属性的详细介绍及相关的例子

    介绍DOM4J的主要接口的使用方法,Elementorg.dom4j.Attribute : Attribute接口定义了XML文件的属性 org.dom4j.Branch:Branch为能够包含子节点的节点如XML元素和文档定义了一个公共为 org.dom4j.CDATA : CDATA定义了...

    dom4j 解析写入xml

    1、xml文档解析 2、 dom4j解析xml 3、实现xml文件解析 xml字符串解析 xml MAP键值对解析 4、实现xml写入与生成文件

    DOM解析 DOM4j解析

    DOM 为创建、处理和操纵 XML 文档提供了灵活和有效的方法,但使用起来可能比较困难并且可能导致不稳定和错误的代码。作者 Parand Tony Daruger 提供了一套 Java 用法模式和函数库,使 DOM 变得健壮且易于使用。

    使用dom4j操作xml

    Dom4j是一个开源的Java XML 解析处理工具,用来读写XML文件,它应用于Java平台,采用了Java集合框架并完全支持DOM, SAX和JAXP。具有性能优异、功能强大和极易使用的特点。可以作为解析XML文档析首先API。本文讲述了...

    dom4j-1.6.1.zip

    Java在使用xPath处理xml文件时需要加载dom4j和jaxen。使用时需要同时引入到库里,版本已经对应好了,是1.61版本的dom4j和相应的jaxen

    xml内容解析,包含命名空间时需特殊处理(dom4j)

    NULL 博文链接:https://superich2008.iteye.com/blog/1750287

    dom4j-big-xml.zip

    使用dom4j处理大xml文件文本

    dom4j使用与jar包

    java处理xml文件的jar包。操作简单方便。可以对xml的节点,值修改等等,详细说明在在下载包中有说明

    Java解除文件占用即Dom4j操作后实现xml关流

    主要介绍了Java解除文件占用即Dom4j操作后实现xml关流,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    dom4j-2.1.0.zip jar包

    java jar包,亲测试可用 ...把一个JAR文件添加到系统的classpath环境变量之后,java通常会把这个JAR文件当做一个路径来处理。通常使用jar命令来压缩,可以把一个或多个路径全部压缩成一个JAR文件。

    dom4j-1.6.1.jar.zip

    java jar包,亲测试可用 ...把一个JAR文件添加到系统的classpath环境变量之后,java通常会把这个JAR文件当做一个路径来处理。通常使用jar命令来压缩,可以把一个或多个路径全部压缩成一个JAR文件。

    dom4j-1.6.1-hudson-3.jar.zip

    java jar包,亲测试可用 ...把一个JAR文件添加到系统的classpath环境变量之后,java通常会把这个JAR文件当做一个路径来处理。通常使用jar命令来压缩,可以把一个或多个路径全部压缩成一个JAR文件。

    java处理BOM头的XML

    java处理BOM头的XML,使用记事本编辑会产生BOM头,这样的XML在dom4j处理时会报异常。

    多文件上传 加水印 生成缩略图 压缩图片 生成xml文档 拆分xml文档 ASP.NET

    本项目的功能是上传文件 处理图片(加水印,生成缩略图,压缩图片) 生成XML文档 拆分XML文档

    XML解析教程

    java解析xml四种方式 DOM (Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的标准接口, DOM采用建立树形...目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件.

    Xml 文件处理 java

    自己写的一个XML文件处理,通过DOM4J,实现了逐一遍历整个XML文件,直到找到找到需要修改节点,退出程序,这个代码里面只调试了updatesinglenodevalue这一个函数,其他要用,自己调试,这个工程不需更改就可以使用,...

    智能家居环境监测系统,定期采集原始环境整理成温度,湿度,二氧化碳数据给中心处理系统

    4、xml(dom4j解析) 5、log4j 功能 1、定期采集(次/小时)原始环境Log文件,整理成温度,湿度,二氧化碳数据清单,并上传给中心处理系统。 2、中心处理系统(接收系统)侦听并收集采集系统发送的数据信息温度和湿度,...

    hibernate核心包

    dom4j-1.6.1.jar dom4j XML 解析器 jta-1.1.jar 标准的 JAVA 事务处理接口 javassist-3.9.0.GA.jar 代码生成工具 (Hibernate用它在运行时扩展 Java类和实现,同cglib包) slf4j-api-1.5.8.jar和slf4j-log4j12-1.5.0....

    cdp4j:cdp4j-适用于Java的Chrome DevTools协议

    您可以轻松下载和上传文本,图像,处理PDF文件或截屏。 与Web应用程序之间的数据导入或导出。 从Web提取任何类型的数据。 自动化Web上传/下载。 主要特点 支持Chrome DevTools协议的全部功能() PDF打印机 ...

Global site tag (gtag.js) - Google Analytics