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

3

我有一个非常大的XML文件,其中包含相对固定大小的条目,例如:

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

  <item>...</item>
  <item>...</item>
<rootElem>
项目元素相对较浅,通常相当小(<100 KB),但可能有很多(数十万个)。这些项目彼此完全独立。
在Java中,我该如何高效地处理文件?我无法将整个文件作为DOM读取,也不想使用SAX,因为代码会变得相当复杂。我想避免将文件分割成较小的部分。
如果我可以一次获取每个项目元素作为单独的DOM文档,那就最好了,这样我就可以使用像JAXB这样的工具进行处理。基本上,我只想循环一次处理所有项目。
我认为这是一个相当常见的问题。
4个回答

3
Java 6拥有StAX支持。它执行类似于SAX的流处理,但使用基于拉的方法,这导致了更简单的处理代码。

1
当输入很大时,通常需要对文档进行顺序处理(也称为流处理)。虽然SAX可能会变得有点混乱(或者至少需要相当多的代码),因为你基本上必须构建一个执行抽取的状态机。如果你寻找基于事件的实现而不是事件驱动的解析器,则可能会发现这种方法稍微简单一些。
另外,你提取项目元素内容的想法也是可行的,可以使用SAX进行第一步操作,并在事件/拉取解析和完全DOM访问的灵活性之间取得可接受的平衡。(尽管它仍然比事件/拉取解析要慢得多,因为它进行了大量分配,但至少不需要同时将所有内容保存在内存中。)

@axtavt已经提出了一个建议。我曾经使用过http://www.xmlpull.org/,但现在不确定它的状态。 - Cumbayah

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);  

            }         

     }  

}

0

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

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接