2015-10-20 13 views
5

Scenario: Ricevo un enorme file xml tramite una rete estremamente lenta, quindi desidero iniziare l'elaborazione in eccesso il prima possibile. Per questo motivo ho deciso di utilizzare SAXParser.Perché SAXParser legge tanto prima di lanciare eventi?

Mi aspettavo che dopo che un tag è finito otterrò un evento.

Il seguente test mostra cosa intendo:

@Test 
public void sax_parser_read_much_things_before_returning_events() throws Exception{ 
    String xml = "<a>" 
       + " <b>..</b>" 
       + " <c>..</c>" 
        // much more ... 
       + "</a>"; 

    // wrapper to show what is read 
    InputStream is = new InputStream() { 
     InputStream is = new ByteArrayInputStream(xml.getBytes()); 

     @Override 
     public int read() throws IOException { 
      int val = is.read(); 
      System.out.print((char) val); 
      return val; 
     } 
    }; 

    SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); 
    parser.parse(is, new DefaultHandler(){ 
     @Override 
     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
      System.out.print("\nHandler start: " + qName); 
     } 

     @Override 
     public void endElement(String uri, String localName, String qName) throws SAXException { 
      System.out.print("\nHandler end: " + qName); 
     } 
    }); 
} 

ho avvolto il flusso di ingresso per vedere ciò che viene letto e quando si verificano gli eventi.

Quello che mi aspettavo era qualcosa di simile:

<a>     <- output from read() 
Handler start: a 
<b>     <- output from read() 
Handler start: b 
</b>     <- output from read() 
Handler end: b 
... 

Purtroppo il risultato è stato seguito:

<a> <b>..</b> <c>..</c></a>  <- output from read() 
Handler start: a 
Handler start: b 
Handler end: b 
Handler start: c 
Handler end: c 
Handler end: a 

Dove è il mio errore e come posso ottenere il risultato atteso?

Edit:

  • La prima cosa è che lui sta cercando di rilevare la versione doc, che fa sì che per eseguire la scansione di tutto. Con la versione doc interviene (ma non dove mi aspetto)
  • Non va bene che "vuole" leggere ad esempio 1000 byte e blocchi per così tanto tempo perché è possibile che quel flusso non contenga così tanto in questo punto del tempo.
  • Ho trovato le dimensioni del buffer in XMLEntityManager:
    • pubblico static finale int DEFAULT_BUFFER_SIZE = 8192;
    • public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
    • public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
+1

Penso che dovresti provare un bug test file - sospetto che una lettura bufferizzata stia effettivamente leggendo l'intero file prima che inizi l'elaborazione perché bufferizzerebbe il file in (diciamo) blocchi di 1k o qualsiasi altra cosa - se usi un file di grandi dimensioni potresti ottenere qualcosa di più come ti aspetti. – Elemental

risposta

2

Sembra che si stanno facendo ipotesi errate su come il I/O funziona. Un parser XML, come la maggior parte dei software, richiederà i dati in blocchi, perché la richiesta di singoli byte da un flusso è una ricetta per un disastro di prestazioni.

Ciò non implica che il buffer debba essere completamente riempito prima che venga restituito un tentativo di lettura. È solo che un ByteArrayInputStream non è in grado di emulare il comportamento di una rete InputStream. Puoi facilmente risolvere ciò sostituendo lo read(byte[], int, int) e non restituendo un buffer completo ma, ad es.un singolo byte a ogni richiesta:

@Test 
public void sax_parser_read_much_things_before_returning_events() throws Exception{ 
    final String xml = "<a>" 
       + " <b>..</b>" 
       + " <c>..</c>" 
        // much more ... 
       + "</a>"; 

    // wrapper to show what is read 
    InputStream is = new InputStream() { 
     InputStream is = new ByteArrayInputStream(xml.getBytes()); 

     @Override 
     public int read() throws IOException { 
      int val = is.read(); 
      System.out.print((char) val); 
      return val; 
     } 
     @Override 
     public int read(byte[] b, int off, int len) throws IOException { 
      return super.read(b, off, 1); 
     } 
    }; 

    SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); 
    parser.parse(is, new DefaultHandler(){ 
     @Override 
     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
      System.out.print("\nHandler start: " + qName); 
     } 

     @Override 
     public void endElement(String uri, String localName, String qName) throws SAXException { 
      System.out.print("\nHandler end: " + qName); 
     } 
    }); 
} 

Questo stamperà

<a> 
Handler start: a<b> 
Handler start: b..</b> 
Handler end: b <c> 
Handler start: c..</c> 
Handler end: c</a> 
Handler end: a? 

mostra, come il parser XML si adatta alla disponibilità di dati dal InputStream.

+2

Il 'read (byte [], int, int)' può essere semplificato come 'return super.read (b, off, 1);'. –

+0

@Didier L: davvero, buona cattura. – Holger

1

Internamente il parser SAX molto probabilmente ha avvolto il tuo InputStream in un BufferedReader o utilizza una sorta di buffer. Altrimenti leggerebbe byte singoli dall'input che farebbe davvero male alle prestazioni.

Quindi ciò che state vedendo è che il parser legge un pezzo dall'input e poi elabora quella parte, il rilascio degli eventi SAX, e così via ...

Problemi correlati