如何在Java中高效地读取由大量小项组成的大型XML文件?

时间:2010-09-06 18:09:09

标签: java xml parsing dom sax

我有一个大型XML文件,由相对固定大小的项目组成,即

<rootElem>
  <item>...</item>

  <item>...</item>
  <item>...</item>
<rootElem>

元素相对较浅且通常相当小(<100 KB),但可能有很多(数十万)。这些项目完全相互独立。

如何在Java中有效地处理文件?我无法以DOM格式读取整个文件,我不喜欢使用SAX,因为代码变得相当复杂。我想避免将文件拆分为较小的部分。

如果我能够一次一个地获取每个 元素作为单独的DOM文档,我可以使用像JAXB这样的工具进行处理。基本上我只想在所有项目上循环一次。

我认为这是一个相当普遍的问题。

4 个答案:

答案 0 :(得分:3)

Java 6有一个StAX support。它执行像SAX这样的流处理,但使用基于拉的方法,这导致更简单的处理代码。

答案 1 :(得分:1)

当输入很大时,文档的顺序(a.k.a.流)处理通常是所要求的。确实,SAX可能会变得有点混乱(或至少需要相当多的代码)因为你基本上必须构建一个状态机来进行提取。如果您寻找XML pull解析器而不是基于事件的实现,您至少可以发现这种方法更容易使用。

您可以在第一步使用SAX来提取项元素的内容,并且可以在使用事件/拉解析和完全DOM访问的灵活性之间取得可接受的平衡。 (它仍然比事件/拉解析慢,做重量分配,但至少要求同时将它全部保存在内存中。)

答案 2 :(得分:0)

我没有尝试过,但是......如果您的XML文件始终具有相同的格式,您可以使用BufferedReader自行解析它们,查找<item>标记,并将项目内容存储在StringBuffer中。然后,您可以使用DOM解析器解析每个字符串(包括作为根的项),并对其进行处理。所有项目只需要一个DocumentBuilder。

该方法的优点是可以快速解析文件而不会出现任何内存问题,并且具有DOM树的便利性。缺点是您不会进行真正的XML解析:如果XML不完全符合您的预期(<item/>可能吗?),您的程序可能会崩溃。

这里的问题是,当您首次解析文件时,需要处理一些XML元素(项目内的元素),就像它们不是XML元素一样。如果你能找到另一种方法,你可以使用SAX来解析文件,以安全的方式将项目内容作为字符串获取,并使用DOM解析器解析项目,如上所述。

我想另一种选择是使用SAX或StAX,并根据相关事件为项目创建DOM树。但如果语言中有许多元素,那可能会很复杂。

答案 3 :(得分:0)

使用DOM,我有一种解析xml的有效方法。我自己编写了这个DOM解析器,使用递归来解析你的xml,而不需要知道单个标记。如果存在,它将按顺序为您提供每个节点的文本内容。您可以删除以下代码中的注释部分以获取节点名称。希望它会有所帮助。

import java.io.BufferedWriter;
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

 import javax.xml.parsers.DocumentBuilder;  
 import javax.xml.parsers.DocumentBuilderFactory;  
 import org.w3c.dom.Document;  
 import org.w3c.dom.Node;  
 import org.w3c.dom.NodeList;  



public class RecDOMP {


public static void main(String[] args) throws Exception{
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
        dbf.setValidating(false); 
        DocumentBuilder db = dbf.newDocumentBuilder();   

// replace following  path with your input xml path  
         Document doc = db.parse(new FileInputStream(new File  ("D:\\ambuj\\input.xml")));  

// replace following  path with your output xml path 
         File OutputDOM = new File("D:\\ambuj\\outapip1.txt");
            FileOutputStream fostream = new FileOutputStream(OutputDOM);
            OutputStreamWriter oswriter = new OutputStreamWriter (fostream);
            BufferedWriter bwriter = new BufferedWriter(oswriter);

            // if file doesnt exists, then create it
            if (!OutputDOM.exists()) {
                OutputDOM.createNewFile();}


            visitRecursively(doc,bwriter);
            bwriter.close(); oswriter.close(); fostream.close();

            System.out.println("Done");
}
public static void visitRecursively(Node node, BufferedWriter bw) throws IOException{  

             // get all child nodes  
         NodeList list = node.getChildNodes();                                  
         for (int i=0; i<list.getLength(); i++) {          
                 // get child node              
       Node childNode = list.item(i);  
       if (childNode.getNodeType() == Node.TEXT_NODE)
       {
   //System.out.println("Found Node: " + childNode.getNodeName()           
    //   + " - with value: " + childNode.getNodeValue()+" Node type:"+childNode.getNodeType()); 

   String nodeValue= childNode.getNodeValue();
   nodeValue=nodeValue.replace("\n","").replaceAll("\\s","");
   if (!nodeValue.isEmpty())
   {
       System.out.println(nodeValue);
       bw.write(nodeValue);
       bw.newLine();
   }
       }
       visitRecursively(childNode,bw);  

            }         

     }  

}