【问题标题】:Parsing a big xml file Java解析一个大的xml文件Java
【发布时间】:2016-03-16 10:26:22
【问题描述】:

我有具有这种结构的大 xml 文件 (~1GB):

<?xml version="1.0" encoding="UTF-8"?>
<GenoExchange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ncbi.nlm.nih.gov/SNP/geno" xsi:schemaLocation="http://www.ncbi.nlm.nih.gov/SNP/geno ftp://ftp.ncbi.nlm.nih.gov/snp/specs/genoex_1_5.xsd" dbSNPBuildNo="146" reportId="MT" reportType="chromosome">
    <Population popId="638" handle="TSC-CSHL" locPopId="TSC_42_AA">
        <popClass self="NORTH AMERICA"/>
    </Population>
 <SnpInfo rsId="1041870" observed="C/T">
        <SnpLoc genomicAssembly="107:GRCh38.p2" geneId="4512" geneSymbol="COX1" chrom="MT" start="6150" locType="2" rsOrientToChrom="fwd" contigAllele="T" contig="NC_012920:1"/>
        <SsInfo ssId="1508548" locSnpId="TSC0349089" ssOrientToRs="fwd">
            <ByPop popId="1303" sampleSize="184">
                <AlleleFreq allele="T" freq="1"/>
                <AlleleFreq allele="C" freq="0"/>
            </ByPop>
        </SsInfo>
    </SnpInfo>
<SnpInfo rsId="1029293" observed="C/T">
        <SnpLoc genomicAssembly="107:GRCh38.p2" geneId="4512" geneSymbol="COX1" chrom="MT" start="6307" locType="2" rsOrientToChrom="fwd" contigAllele="C" contig="NC_012920:1"/>
        <SsInfo ssId="1494519" locSnpId="TSC0254145" ssOrientToRs="fwd">
            <ByPop popId="639" sampleSize="82">
                <AlleleFreq allele="T" freq="0"/>
                <AlleleFreq allele="C" freq="1"/>
            </ByPop>
            <ByPop popId="1303" sampleSize="184">
                <AlleleFreq allele="T" freq="0"/>
                <AlleleFreq allele="C" freq="1"/>
            </ByPop>
        </SsInfo>
    </SnpInfo>

我想找到一个特定的 rsID,例如 rsID="1029293" 并提取该节点内的所有信息。我不想运行所有文件。我只想找到该 ID,提取该信息并结束迭代。 根据我的阅读,如果我使用 SAX 或 Stax 解析器会更好。我正在使用 SAX,这是我的代码:

class UserHandler extends DefaultHandler {

   String rsID = null;
   String i = "1029293";       

   @Override
   public void startElement(String uri, 
      String localName, String qName, Attributes attributes) throws SAXException {

      if (qName.equalsIgnoreCase("SnpInfo")) { 
         rsID = attributes.getValue("rsId"); 
          //System.out.println("value: " + rsID);
      }
      if((i).equals(rsID) &&
         qName.equalsIgnoreCase("SnpInfo")){
         System.out.println("Start Element: " + qName + " " + rsID);
      }      

      if ((i).equals(rsID) && qName.equalsIgnoreCase("SsInfo")) {
          String a = attributes.getValue("ssId");
          System.out.println("SSID: " + a);
      }

      if ((i).equals(rsID) && qName.equalsIgnoreCase("ByPop")) {
          String p = attributes.getValue("popId");
          System.out.println("POPID: " + p);
      } 
      if ((i).equals(rsID) && qName.equalsIgnoreCase("AlleleFreq")) {
          String p = attributes.getValue("allele");
          String f = attributes.getValue("freq"); 
          System.out.println("ALLELE: " + p + " FREQ: " + f);
      }  
      if ((i).equals(rsID) && qName.equalsIgnoreCase("GTypeFreq")) {
          String p = attributes.getValue("gtype");
          String f = attributes.getValue("freq"); 
          System.out.println("GTYPE: " + p + " FREQ: " + f);
      }  
   }

   @Override
   public void endElement(String uri, 
      String localName, String qName) throws SAXException {
      if (qName.equalsIgnoreCase("SnpInfo")) {
         if((i).equals(rsID) 
            && qName.equalsIgnoreCase("SnpInfo"))
            System.out.println("End Element: " + qName); 
         }
      }
}
public class XMLParser {

    public static void main(String argv[]) {
        try {   
            InputStream fileStream = new FileInputStream("/home/xml/gt_chr10.xml.gz");
            InputStream gzipStream = new GZIPInputStream(fileStream);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();
            UserHandler userhandler = new UserHandler();
            saxParser.parse(gzipStream, userhandler);
        } catch (Exception e) {
            e.printStackTrace();
        }
    } 

我的问题是我的代码在整个文件中搜索 ID,并且每次都需要 2 分钟以上。我不能有一个需要这么长时间的代码。 有没有更好的方法?

【问题讨论】:

  • 扔掉所有代码,使用由 SAX 或 STAX 提供的 XPath。注意你不应该继续测试i.equals(rsID),或者使用多余的括号。
  • 是否可以将 XPath 与 SAX 或 STAX 一起使用?我以前从未使用过 xml 文件,所以我唯一知道的是我在论坛中找到的。但是很多人说 XPath 需要像 DOM 这样的方法才能工作。

标签: java xml parsing xml-parsing


【解决方案1】:

使用 STAX 可以在解析 XML 时提供更多控制权,因为您会主动从流中提取元素。这样你就可以拉出下一个事件,处理它,一旦你找到你的数据,简单地终止循环(如果你必须使用一个标志,甚至使用一个返回语句)

InputStream in = ...
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader eventReader = factory.createXMLEventReader(in);

boolean found = false;
while (!found && eventReader.hasNext()) {
    XMLEvent event = eventReader.nextEvent();
    switch (event.getEventType()) {
    case XMLStreamConstants.START_ELEMENT:
        // your logic here 
        // once you found your element, you can terminate the loop 
        found = true;
        break;
    case XMLStreamConstants.END_ELEMENT:
        // your logic here
        break;
    }
}

(为了简洁,省略了异常和资源处理)

顺便说一句,通过将if ((i).equals(rsID) &amp;&amp; ... 合并为一个,您将获得一些性能,并在嵌套 ifs

中进行详细检查
if ((i).equals(rsID)) {
    if(qName.equalsIgnoreCase("GTypeFreq")) {
       ...
    }
}

【讨论】:

    【解决方案2】:

    您可以在结束元素处理程序中引发异常,以向解析器指示它中止解析 (http://www.ibm.com/developerworks/library/x-tipsaxstop/):

       @Override
       public void endElement(String uri, 
          String localName, String qName) throws SAXException {
          if (qName.equalsIgnoreCase("SnpInfo")) {
             if((i).equals(rsID) 
                && qName.equalsIgnoreCase("SnpInfo"))
                System.out.println("End Element: " + qName); 
                throw SAXException("Element found.");
             }
          }
    

    【讨论】:

      【解决方案3】:

      避免每次运行时解析整个文件的唯一方法是将数据放入 XML 数据库中。解析一个 1Gb 的文件大约需要一分钟,上下浮动取决于您的机器速度以及您在每个节点上执行的处理。

      流式 XSLT 3.0 解决方案很简单:

      <xsl:transform version="3.0"
           xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
           xpath-default-namespace="http://www.ncbi.nlm.nih.gov/SNP/geno">
        <xsl:template name="xsl:initial-template">
          <xsl:stream href="input.xml">
             <xsl:copy-of select="/GenoExchange/SnpInfo[@rsId='1041870'][1]"/>
          </xsl:stream>
        </xsl:template>
      </xsl:transform>
      

      无需编写所有讨厌的 SAX 或 StAX 代码。

      我放入了“[1]”谓词以允许处理器在找到第一个命中后放弃搜索。

      【讨论】:

      • 当我使用它时,我总是收到消息“未找到元素”。
      • 它对我有用。也许您忘记将 xsl:initial-template 指定为入口点,或者您的源文件未调用 input.xml,或者未启用 XSLT 3.0。
      【解决方案4】:

      最好的方法是使用vtd-xmlxpath... 1GB xml 文件大约需要 1.5GB 堆空间,并且在 3~4 年的英特尔处理器中需要

      import com.ximpleware.*;
      
          public class simpleXpathSearch{
              public  static  void main(String s[]) throws VTDException,java.io.UnsupportedEncodingException,java.io.IOException{
                  VTDGen vg = new VTDGen();
                  vg.setLCLevel(5);
                  if (!vg.parseFile("input.xml", false))
                      return;
                  VTDNav vn = vg.getNav();
                  AutoPilot ap = new AutoPilot(vn);
                  ap.selectXPath("/*/*[@rsID='1029293']");
                  int i=0;
                  while((i=ap.evalXPath())!=-1){
                     // your code logic here
                  }
      

      【讨论】:

      • 我试过这个例子,它适用于小于 1GB 的文件,但对于更大的文件,我得到:java.lang.OutOfMemoryError: Java heap space
      • 我不明白如何在不先解析文件的情况下创建 vtd-xml。由于我只有 4GB 的 RAM,我无法解析我的文件。
      • 但是我有超过 10GB 的文件,这就是我的问题所在。
      • 如果你只有 4GB,那么你别无选择,只能使用 StAX 来处理那个 10GB 的文件……但同样,你为什么不能生成比这更小的文件呢?每个文件 1GB 相当合理,不是吗?
      • 文件不是我的。感谢您的帮助。
      【解决方案5】:

      //主类

      public static void main(String[] args) {
          SAXReader.read();
      }
      

      //SAXReader

      public static void read(){
          try {
              XMLReader processor = XMLReaderFactory.createXMLReader();
              processor.setContentHandler(new SAXController());
              processor.parse(new InputSource("MyXML.xml"));
          } catch (SAXException | IOException e) {
              System.err.println(e.getMessage());
          }
      }
      

      //SAX控制器

      // SAXController 扩展了 DefaultHandler

      private int tab = 0;
      
      private void tabulation() {
          for (int i=0; i<tab; i++)
              System.out.print("  ");
      }
      
      @Override
      public void startDocument() {
          tabulation();
          System.out.println("Starting XML Document");
          tab++;
      }
      
      @Override
      public void endDocument() {
          tab--;
          tabulation();
          System.out.println("Ending XML Document");
      }
      
      @Override
      public void startElement(String uri, String localName, String qName, Attributes attributes)
              throws SAXException {
          tabulation();
          System.out.print(localName);
          if (attributes.getLength()>0) {
              for (int i=0; i<attributes.getLength(); i++) {
                  System.out.print(attributes.getLocalName(i)+": "+attributes.getValue(i));
              }
          }
          System.out.println();
          tab++;
      }
      
      @Override
      public void endElement(String uri, String localName, String qName)
              throws SAXException {
          tab--;
          tabulation();
          System.out.println(localName);
      }
      
      @Override
      public void characters(char[] ch, int start, int length)
              throws SAXException {
          String content= new String(ch, start, length);
          content= content.replaceAll("[\t\n]", "").trim();
          if (!content.equals("")) {
              tabulation();
              System.out.println(content);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-27
        • 2015-08-12
        • 2015-05-14
        • 1970-01-01
        • 1970-01-01
        • 2013-11-22
        • 2011-05-09
        相关资源
        最近更新 更多