# Handler-Wechsel, SAX Parser bricht nach 8192 Byte ab



## Rusti (30. Sep 2011)

Hallo Gemeinde,

ich habe ein Problem beim Parsen von XML Dokumenten mittels des SAXParsers. Ich nutze den Parser zunächst mit einem allgemeinen Handler, welcher anhand des ersten erkannten Objektes einen speziellen Handler aufruft:

Methode zum Parsen:

```
public static Object parseXML(byte[] b) {
		ByteArrayInputStream bs = new ByteArrayInputStream(b);
		SAXParserFactory factory = SAXParserFactory.newInstance();
		try {
			SAXParser parser = factory.newSAXParser();
			XML_Handler handler = new XML_Handler(parser, bs);
			parser.parse(bs, handler);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			System.out.println("Fehler beim Parsen des Dokuments!");
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return parsing_result;
	}
```

Der allgemeine Handler:

```
public class XML_Handler extends DefaultHandler{
	
	public SAXParser parser;
	public ByteArrayInputStream bais;

	
	public XML_Handler(SAXParser psr, ByteArrayInputStream bs) {
		this.parser = psr;
		this.bais = bs;
	}

	@Override
	public void startDocument() throws SAXException {
		System.out.println("New Document started...");
	}
	/**
	 * Bei Erkennung eines Dokuments wird ein spezialisierter Handler an den Parser übergeben.
	 */
	@Override
	public void startElement(String uri, String localName,
			String qName, Attributes atts) throws SAXException {
		System.out.format("%s found!\n",qName);
		try {
			if ( qName.equals("Resolution_Data") ) {
                           this.parser.getXMLReader().setContentHandler(new ResData_Handler());
			}
			else if ( qName.equals("Scheduling_Data") ) {
                           this.parser.getXMLReader().setContentHandler(new                   
                           SchedData_Handler());
			}

                 ...............

			else {
				System.out.format("Kein Parser für den Dokumenttyp %s gefunden!",qName);
				this.parser.getXMLReader().setContentHandler(null);
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
```

Die speziellen Handler machen nicht aufregendes, sie sortieren die Gefundenen Elemente bzw. deren Inhalt mittels startElement() und characters() in die entsprechenden Datenstrukturen. Alles funktioniert wunderbar mit kleinen Dateien (<8kb).

Bei Dateien, welche die Kapazität des char[]-Arrays allerdings überschreiten wird es problematisch.
Wenn ich während des Parse-Vorgangs den Content Handler wechsle, wird nach Beendigung des aktuellen "char[]" Arrays kein neues mehr angefangen und der Vorgang bricht mit "XML document structures must start and end within the same entity." ab... Habe die Sache inzwischen soweit eingegrenzt, dass die Datei nach 8192Byte schlicht nicht weiter eingelesen wird.
Wenn ich diesen Hanlderwechsel nicht durchführe läuft der Parser ohne murren durch und beginnt ohne Weiteres die nächsten 8192 Byte.

Ich finde zu der Thematik leider nicht einmal Anhaltspunkte im Netz.
Kennt sich hier evtl. jemand mit der Geschichte aus und hat ein paar Hinzweise für mich? 

Beste Grüße

Rusti


----------



## musiKk (30. Sep 2011)

Kannst Du die Handler vielleicht noch zeigen?

Weiß aber noch nicht, ob das hilft. Ideal wäre natürlich ein möglichst kurzes Beispiel, welches den Fehler demonstriert (bei der XML-Datei sollte eine Strukturbeschreibung reichen; 8kb sind für einen Post vielleicht etwas viel).


----------



## Rusti (30. Sep 2011)

Die Handler haben alle eine identische Form, der der zum Absturz führt ist der folgende:


```
package handlers;

import java.util.ArrayList;
import java.util.List;

import model.XML_Parser;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import sections.DBTSection;
import structures.Descriptor;
import structures.Scheduling_Information;
import structures.Table;
import structures.Descriptor.Descriptor_Content;
import structures.Scheduling_Information.Content_Provider;
import structures.Scheduling_Information.Programme;

public class Scheduling_Information_Handler extends DefaultHandler {
	
	public Scheduling_Information schi;
	public int cumlength;
	public int num_sections; 
	public int listlength; 
	public int bitcounter; 
	public int dsc_length; 
	public Table dbt;
	/**
	 * Eine Liste, welchen den Pfad des aktuellen Objektes enthält, z.B.
	 * { program_map_table, pmt_section, elementary_stream, es_descriptor, descriptor_tag }
	 */
	public List<String> current_object;

	public Scheduling_Information_Handler () {
		this.schi = new Scheduling_Information();
		this.dbt = new Table("DBT",68,1);
		this.current_object = new ArrayList<String>();
		current_object.add("scheduling_information");
	}
	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		current_object.add(qName);

		if ( qName.equals("content_provider") ) {
			schi.content_providers.add(schi.new Content_Provider());
		}
		else if ( qName.equals("programme") ) {
			Content_Provider actprv = schi.content_providers.get(schi.content_providers.size()-1);
			actprv.programmes.add(schi.new Programme());
		}

               ............................. Hier kommen noch einige, die immer tiefer verschachtelte Strukturen aufbauen.....................................

	}
	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		current_object.remove(current_object.size()-1);

	}
	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		if ( ch[start] == '\n' ) return;
		String curobj = current_object.get(current_object.size()-1);
		String prvobj = current_object.get(current_object.size()-2);
		StringBuilder result = new StringBuilder();
		for (int i=start; i < (start + length); i++) result.append(ch[i]);
		Content_Provider actprv = schi.content_providers.get(schi.content_providers.size()-1);
		
		if ( curobj.equals("event_component_id") ) {
			Programme actpro = actprv.programmes.get(actprv.programmes.size()-1);
			Scheduling_Information.Event actevt = actpro.events.get(actpro.events.size()-1);
			Scheduling_Information.Event_Component actcmp = actevt.event_components.get(actevt.event_components.size()-1);
			actcmp.event_component_id = Integer.parseInt(result.toString());
		}
		else if ( curobj.equals("dbt_id") ) {
			actprv.dbt_id = Integer.parseInt(result.toString());
		}

.................................. hier werden dann die Inhalte der Elemente in die oben erzeugte Datenstruktur eingelesen.................................................


@Override
	public void endDocument() throws SAXException {

.............. Hier erfolgt das setzen von Werten in der aufgebauten Datenstruktur, z.b. Bestimmung der Länge der erzeugten Substrukturen.............................
}
```

Die endDocument() habe ich mal rausgeschnitten, da kommt der Parser aber auch definitiv nicht mehr an. Ich habe mich soweit rangetastet, dass der Abbruch beim Erreichen des Endes des char[] Arrays auftritt. Normalerweise liest des Parser ja zum Ende hin einfach den nächsten Teil der Quelldatei ein. Nach einem Wechsel des Handlers ist aber wie gesagt Schluss wenn er das Ende erreicht. Schonmal vielen Dank fürs angucken!

Rusti


----------



## SlaterB (30. Sep 2011)

meiner Philosphie nach sollte man so eine Blackbox wie den Parser so wenig wie nötig belasten,
warum musst du den Handler wechseln, was macht ein Handler schon mehr als auf die paar Tag-Ereignisse zu reagieren?

du köntest doch einen Handler X setzen, der alle Events an einen intern aktuell gesetzten zweiten Handler weiterleitet,
anfangs an den XML_Handler, später dann evtl. ResData_Handler oder was anderes,

offensichtlich kann es dann an sich nicht zu einem derartigen Fehler kommen, X bleibt gleich, 
was innerhalb von X passiert ist vollständig unter deiner Kontrolle, dem Parser dürfte das allen Naturgesetzen nach ganz egal sein,


----------



## Rusti (3. Okt 2011)

@SlaterB:

Das ist schonmal eine gute Idee, sollte wie Du sagst nach allen gängigen Gesetzen funktionieren.
Ich werde die Problematik erstmal auf diese Weise umschiffen, auch wenn das ganze natürlich einen deutlichen Verlust an "Eleganz" mit sich zieht... 

In der JavaDoc zum Parser steht ausdrücklich "Applications may register a new or different handler in the middle of a parse, and the SAX parser must begin using the new handler immediately."

Also müsste da doch was zu machen sein #]

Auf jeden Fall vielen Dank!

Rusti


----------



## Rusti (3. Okt 2011)

.....steinigt mich, bewerft mich mit Mist!
Stelle gerade fest, dass die Problematik dadurch entsteht, dass ich das Dokument anderer Stelle noch auf falsche und kaputte Weise validiere... wenn ich die Geschichte raus nehme flutscht auch der Handlerwechsel!

Also nochmals vielen Dank und Entschuldigung


----------

