dom4j處理超大XML

 英文原文 : http://dom4j.sourceforge.net/dom4j-1.6.1/faq.html#large-doc

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()方法保存改。
下面是示例代碼:


Java代碼 複製代碼 收藏代碼
  1. SAXReader reader = new SAXReader();   
  2. reader.addHandler( "/ROWSET/ROW",   
  3.     new ElementHandler() {   
  4.         public void onStart(ElementPath path) {   
  5.             // do nothing here...       
  6.         }   
  7.         public void onEnd(ElementPath path) {   
  8.             // process a ROW element   
  9.             Element row = path.getCurrent();   
  10.             Element rowSet = row.getParent();   
  11.             Document document = row.getDocument();   
  12.             ...   
  13.             // prune the tree   
  14.             row.detach();   
  15.         }   
  16.     }   
  17. );   
  18.     
  19. Document document = reader.read(url);   
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()
動手一試,問題搞定,看來牛皮沒白吹。下面是示例代碼:


Java代碼 複製代碼 收藏代碼
  1.   
  2. /**  
  3.   * 數據寫入xml文件  
  4.   * @param filePath 目標xml文件的存放路徑  
  5.   * @return  
  6.   */  
  7. public boolean writeXML(String filePath){   
  8.   XMLWriter out;   
  9.   try {   
  10.      
  11.    /*  
  12.     * 創建XMLWriter對象,設置XML編碼,解決中文問題。  
  13.     */  
  14.    OutputFormat outputFormat = OutputFormat.createPrettyPrint();   
  15.    outputFormat.setEncoding("GBK");   
  16.    out = new XMLWriter(new FileWriter(filePath),outputFormat);   
  17.      
  18.      
  19.    out.startDocument();   
  20.    Element rootElement = DocumentHelper.createElement("mans");   
  21.    out.writeOpen(rootElement);   
  22.      
  23.    /*  
  24.     * 向mans節點寫入子節點  
  25.     */  
  26.    for(int i=0 ; i<1000000 ; i++){   
  27.     Element man = createManElement(new Long(i), "shuhang"+i);//用於創建節點的方法   
  28.     out.write(man);   
  29.     System.out.println(" the loop index is : " + i);   
  30.    }   
  31.      
  32.    out.writeClose(rootElement);   
  33.    out.endDocument();   
  34.      
  35.    out.close();   
  36.    return true;   
  37.   } catch (IOException e) {   
  38.    e.printStackTrace();   
  39.    return false;   
  40.   } catch (SAXException e) {   
  41.    e.printStackTrace();   
  42.    return false;   
  43.   }   
  44. }  
/**
  * 數據寫入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的註冊事件處理器應該用的是模板方法,通過鉤子將用戶的操作加入到模板中去。何不自己寫一個事件處理器來完成我們自己的定製操作,代碼如下:

Java代碼 複製代碼 收藏代碼
  1. public class ManElementHandler implements ElementHandler {   
  2. public String mdbName;   
  3. ­   
  4. public ManElementHandler(){   
  5.      
  6. }   
  7. ­   
  8. public ManElementHandler(String mdbName){   
  9.   this.mdbName = mdbName;   
  10. }   
  11. ­   
  12. public boolean saveMan(Element element, String mdbName){   
  13.   return true;   
  14. }   
  15. ­   
  16. public void onEnd(ElementPath arg0) {   
  17.          Element row = arg0.getCurrent();   
  18.          Element rowSet = row.getParent();   
  19.          Document document = row.getDocument();   
  20.          Element root = document.getRootElement();   
  21.   Iterator it = root.elementIterator();   
  22.   while(it.hasNext()){   
  23.    Element element = (Element)it.next();   
  24.    System.out.println(" id : " + element.elementText("id") + " name : " + element.elementText("name"));   
  25.    saveMan(element, this.mdbName);   
  26.   }   
  27.          row.detach();   
  28. }   
  29. public void onStart(ElementPath path) {   
  30.      
  31. }   
  32. }  
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未發生內存溢出。


Java代碼 複製代碼 收藏代碼
  1. //*****************************************************************************************************************   
  2. package com;   
  3. import java.sql.Connection;   
  4. import java.sql.DriverManager;   
  5. /**   
  6. * 獲取Access數據庫的連接   
  7. @author 佛山無影腳   
  8. @version 1.0  
  9. * Jul 72008  4:35:49 PM   
  10. */   
  11. public class AccessMDBUtil {   
  12. public static Connection connectMdb(String mdbName) {   
  13.   if(mdbName == null || mdbName.equals("")){   
  14.    return null;   
  15.   }   
  16.   try {   
  17.    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");   
  18.    String dburl ="jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ="+mdbName;   
  19.    Connection conn=DriverManager.getConnection(dburl);   
  20.    return conn;   
  21.   } catch (Exception e) {   
  22.    e.printStackTrace();   
  23.    return null;   
  24.   }   
  25. }   
  26. }   
  27. //*******************************************************************************************************************   
  28. //*******************************************************************************************************************   
  29. package com;   
  30. import java.sql.Connection;   
  31. import java.sql.ResultSet;   
  32. import java.sql.SQLException;   
  33. import java.sql.Statement;   
  34. import java.util.Iterator;   
  35. import org.dom4j.Document;   
  36. import org.dom4j.Element;   
  37. import org.dom4j.ElementHandler;   
  38. import org.dom4j.ElementPath;   
  39. /**   
  40. * 定製的事件處理器   
  41. @author 佛山無影腳   
  42. @version 1.0  
  43. * Jul 72008  4:35:49 PM   
  44. */   
  45. public class ManElementHandler implements ElementHandler {   
  46. public String mdbName;//數據庫名稱 含路徑   
  47. ­   
  48. public ManElementHandler(){   
  49.      
  50. }   
  51. ­   
  52. public ManElementHandler(String mdbName){   
  53.   this.mdbName = mdbName;   
  54. }   
  55. ­   
  56. /**  
  57.   *將Element節點數據保存到數據庫  
  58.   *@param element 遍歷XML文檔時的當前節點  
  59.   *@param mdbName 數據庫名稱 含路徑  
  60.   *@return  
  61.   */  
  62. public boolean saveMan(Element element, String mdbName){   
  63.   System.out.println(" the method saveMan in ManElementHandler is execute!");   
  64.   Statement stmt = null;   
  65.   ResultSet rs = null;   
  66.   Connection conn = null;   
  67.   try {   
  68.    conn= AccessMDBUtil.connectMdb(mdbName);   
  69.    stmt=conn.createStatement();   
  70.    String sql = "insert into mans(id,name) values(" + element.elementText("id") + ",'" + element.elementText("name") + "')";   
  71.    stmt.executeUpdate(sql);   
  72.      
  73.   } catch (Exception e) {   
  74.    e.printStackTrace();   
  75.    return false;   
  76.   } finally {   
  77.    if(rs != null) {   
  78.     try {   
  79.      rs.close();   
  80.     } catch (SQLException e) {   
  81.      // TODO Auto-generated catch block   
  82.      e.printStackTrace();   
  83.     }   
  84.    }   
  85.    if(stmt != null) {   
  86.     try {   
  87.      stmt.close();   
  88.     } catch (SQLException e) {   
  89.      // TODO Auto-generated catch block   
  90.      e.printStackTrace();   
  91.     }   
  92.    }   
  93.    if(conn != null) {   
  94.     try {   
  95.      conn.close();   
  96.     } catch (SQLException e) {   
  97.      // TODO Auto-generated catch block   
  98.      e.printStackTrace();   
  99.     }   
  100.    }   
  101.      
  102.   }   
  103.      
  104.   return true;   
  105. }   
  106. ­   
  107. public void onEnd(ElementPath arg0) {   
  108.          Element row = arg0.getCurrent();   
  109.          Element rowSet = row.getParent();   
  110.          Document document = row.getDocument();   
  111.          Element root = document.getRootElement();   
  112.   Iterator it = root.elementIterator();   
  113.   while(it.hasNext()){   
  114.    Element element = (Element)it.next();   
  115.    System.out.println(" id : " + element.elementText("id") + " name : " + element.elementText("name"));   
  116.    saveMan(element, this.mdbName);   
  117.   }   
  118.           row.detach();   
  119. }   
  120. public void onStart(ElementPath path) {   
  121.      
  122. }   
  123. }   
  124. //*************************************************************************************************************************   
  125. //*************************************************************************************************************************   
  126. package com;   
  127. import java.io.File;   
  128. import java.io.FileWriter;   
  129. import java.io.IOException;   
  130. import org.dom4j.Document;   
  131. import org.dom4j.DocumentException;   
  132. import org.dom4j.DocumentHelper;   
  133. import org.dom4j.Element;   
  134. import org.dom4j.io.OutputFormat;   
  135. import org.dom4j.io.SAXReader;   
  136. import org.dom4j.io.XMLWriter;   
  137. import org.xml.sax.SAXException;   
  138. import org.xml.sax.XMLReader;   
  139. /**   
  140. * 測試   
  141. @author 佛山無影腳   
  142. @version 1.0  
  143. * Jul 72008  4:35:49 PM   
  144. */   
  145. public class ManTest {   
  146. /**  
  147.   * 創建man節點  
  148.   * <man><id>1</id><name>佛山無影腳</name></man>  
  149.   * @param id  
  150.   * @param name  
  151.   * @return  
  152.   */  
  153. public Element createManElement(Long id,String name){   
  154.   Element manElement = DocumentHelper.createElement("man");   
  155.   manElement.addElement("id").addText(id.toString());   
  156.   manElement.addElement("name").addText(name);   
  157.   return manElement;   
  158. }   
  159. ­   
  160. /**  
  161.   * 數據寫入xml文件  
  162.   * @param filePath 目標xml文件的存放路徑  
  163.   * @return  
  164.   */  
  165. public boolean writeXML(String filePath){   
  166.   XMLWriter out;   
  167.   try {   
  168.      
  169.    /*  
  170.     * 創建XMLWriter對象,設置XML編碼,解決中文問題。  
  171.     */  
  172.    OutputFormat outputFormat = OutputFormat.createPrettyPrint();   
  173.    outputFormat.setEncoding("GBK");   
  174.    out = new XMLWriter(new FileWriter(filePath),outputFormat);   
  175.      
  176.      
  177.    out.startDocument();   
  178.    Element rootElement = DocumentHelper.createElement("mans");   
  179.    out.writeOpen(rootElement);   
  180.      
  181.    /*  
  182.     * 向mans節點寫入子節點  
  183.     */  
  184.    for(int i=0 ; i<1000000 ; i++){   
  185.     Element man = this.createManElement(new Long(i), "shuhang"+i);   
  186.     out.write(man);   
  187.     System.out.println(" the loop index is : " + i);   
  188.    }   
  189.      
  190.    out.writeClose(rootElement);   
  191.    out.endDocument();   
  192.      
  193.    out.close();   
  194.    return true;   
  195.   } catch (IOException e) {   
  196.    e.printStackTrace();   
  197.    return false;   
  198.   } catch (SAXException e) {   
  199.    e.printStackTrace();   
  200.    return false;   
  201.   }   
  202. }   
  203. ­   
  204. /**  
  205.   * 從xml文件中讀取數據,並將數據寫入Access數據  
  206.   * @param filePath xml文件的存放路徑  
  207.   * @return  
  208.   */  
  209. public boolean readXML(String filePath){   
  210.      
  211.   ManElementHandler manElementHandler = new ManElementHandler("F:\\dom4j\\xmlTest.mdb");   
  212.   SAXReader reader = new SAXReader();   
  213.   reader.addHandler( "/mans/man", manElementHandler);   
  214.   Document document = null;   
  215.      try {   
  216.       File file = new File(filePath);   
  217.    document = reader.read(file);   
  218.   } catch (DocumentException e) {   
  219.    e.printStackTrace();   
  220.    return false;   
  221.   }   
  222.   return true;   
  223. }   
  224. ­   
  225. ­   
  226. public static void main(String[] args){   
  227.   XMLReader reader = null;   
  228.   long startTime = System.currentTimeMillis();   
  229.   ManTest mantest = new ManTest();   
  230.      
  231.   mantest.writeXML("f:\\dom4j\\mans.xml");   
  232.   mantest.readXML("f:\\dom4j\\mans.xml");   
  233.      
  234.   long endTime = System.currentTimeMillis();   
  235.   System.out.println(" is end! the millis is : " + (endTime - startTime));   
  236.      
  237. }   
  238. }   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章