2015-10-20 5 views
5

Szenario: Ich erhalte eine riesige XML-Datei über extrem langsames Netzwerk, also möchte ich so früh wie möglich die übermäßige Verarbeitung starten. Aus diesem Grund habe ich mich für SAXParser entschieden.Warum liest SAXParser so viel vor dem Werfen von Ereignissen?

Ich erwartete, dass nach einem Tag wird ein Ereignis erhalten.

Der folgende Test zeigt, was ich meine:

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

ich den Eingangsstrom eingewickelt, um zu sehen, was gelesen wird, und wenn die Ereignisse auftreten.

Was ich erwartet hatte so etwas wie das war:

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

Leider wurde das Ergebnis folgendes:

<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 

Wo ist mein Fehler, und wie kann ich das erwartete Ergebnis zu bekommen?

Edit:

  • Erste Sache ist, dass er versucht, die doc-Version zu erkennen, was alles scannen verursacht. Mit Doc-Version bricht er dazwischen (aber nicht wo ich es erwarte)
  • Es ist nicht in Ordnung, dass er zum Beispiel 1000 Bytes lesen will und so lange blockiert, weil es möglich ist, dass der Stream nicht so viel enthält Zeitpunkt.
  • fand ich die Puffergrößen in XMLEntityManager:
    • public static final int DEFAULT_BUFFER_SIZE = 8192;
    • public static endgültig int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
    • public static endgültig int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
+1

Ich denke, Sie sollten versuchen, eine Bugger-Testdatei - Ich vermute, dass eine gepufferte Lese effektiv liest Ihre gesamte Datei vor der Verarbeitung, weil es die Datei in (sagen wir) 1k Chunks oder was auch immer - wenn Sie eine große Datei verwenden würde Sie können etwas mehr bekommen, als Sie erwarten. – Elemental

Antwort

2

Es scheint, dass Sie falsche Annahmen machen darüber, wie die I/O funktioniert. Ein XML-Parser fordert, wie die meisten Programme, Daten in Blöcken an, da die Anforderung einzelner Bytes aus einem Stream ein Rezept für ein Performance-Desaster ist.

Dies bedeutet nicht, dass der Puffer vollständig gefüllt werden muss, bevor ein Leseversuch zurückkehrt. Es ist nur so, dass eine ByteArrayInputStream das Verhalten eines Netzwerks InputStream nicht emulieren kann. Sie können dies leicht beheben, indem Sie die read(byte[], int, int) überschreiben und keinen vollständigen Puffer zurückgeben, sondern z.ein einziges Byte bei jeder Anfrage:

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

Diese

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

zeigt, wie der XML-Parser paßt sich an die Verfügbarkeit von Daten aus den InputStream gedruckt werden.

+2

Das 'read (byte [], int, int)' kann vereinfacht werden als 'return super.read (b, off, 1);'. –

+0

@Didier L: In der Tat, guter Fang. – Holger

1

Intern wird der SAX-Parser höchstwahrscheinlich Ihre Input in einem BufferedReader oder verwendet eine Art Puffer hat gewickelt. Sonst würde es einzelne Bytes von der Eingabe lesen, was die Leistung wirklich beeinträchtigen würde.

Also, was Sie sehen, ist, dass der Parser ein Stück aus dem Eingang liest und verarbeitet dann diesen Teil, die SAX Ereignisse Ausstellung, und so weiter ...