Filter

(Auszug aus "XML in a Nutshell" von Elliotte Rusty Harold & W. Scott Means)

Ein SAX-Filter befindet sich zwischen dem Parser und der Anwendung, die den Parser aufgerufen hat. Er fängt die Nachrichten ab, die zwischen den beiden Objekten ausgetauscht werden. Die Nachrichten kann der Filter unverändert lassen, oder er kann sie ändern, ersetzen oder blockieren. Auf die Anwendung, die den Parser aufgerufen hat, wirkt der Filter wie ein Parser, d.h. ein XMLReader. Für den Parser sieht der Filter wie eine Client-Anwendung, d.h. ein ContentHandler, aus.

SAX-Filter werden importiert, indem man eine Unterklasse der Klasse org.xml.sax.helpers.XMLFilterImpl schreibt.

Anmerkung: Es gibt auch ein Interface org.xml.sax.XMLFilter. Dieses Interface verhält sich aber für die meisten Anwendungsfülle genau verkehrt herum. Es filtert Nachrichten vom Client an den Parser, aber nicht die viel wichtigeren Nachrichten vom Parser an den Client. Außerdem ist es viel mehr Arbeit, das Interface XMLFilter direkt zu implementieren, als eine Unterklasse von XMLFilterImpl zu schreiben. Erfahrene SAX-Programmierer würden XMLFilter so gut wie nie direkt implementieren, sondern eher eine Unterklasse der Adapterklasse XMLFilterImpl schreiben.

Diese Klasse implementiert alle erforderlichen SAX-Interfaces, d.h. alle Interfaces für den Parser und die Client-Anwendung. Ihre Signatur sieht also folgendermaßen aus:

public class XMLFilterImpl implements XMLFilter, XMLReader, ContentHandler, DTDHandler, ErrorHandler

Zur Erstellung von eigenen Filtern erweitern Sie diese Klasse und überschreiben die Methoden, die sich auf die zu filternden Nachrichten beziehen. Wenn Sie zum Beispiel alle Verarbeitungsanweisungen herausfiltern möchten, würden Sie einen Filter schreiben, der die Methode processingInstruction( ) so überschreibt, dass sie nichts zurückgibt, wie im folgenden Code-Beispiel gezeigt wird.

import org.xml.sax.helpers.XMLFilterImpl;

public class ProcessingInstructionStripper extends XMLFilterImpl {

  public void processingInstruction(String target, String data) {
    // Weil hier nichts gemacht wird, werden Verarbeitungsanweisungen aus dem
    // Dokument *nicht* an den Client weitergegeben
  }

}

Code-Beispiel: Ein SAX-Filter, der Verarbeitungsanweisungen entfernt

Wenn Sie stattdessen eine Verarbeitungsanweisung durch ein Element ersetzen wollen, dessen Name der gleiche ist wie der des Ziels der Verarbeitungsanweisung und dessen Inhalt den Daten der Verarbeitungsanweisung entspricht, rufen Sie aus der Methode processingInstruction( ) die Methoden startElement( ), characters( ) und endElement( ) auf, nachdem Sie die Argumente mit den entsprechenden Daten der Verarbeitungsanweisung gefüllt haben. Dies wird im folgenden Code-Beispiel gezeigt:

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class ProcessingInstructionConverter extends XMLFilterImpl {

  public void processingInstruction(String target, String data)
   throws SAXException {

    // AttributesImpl ist eine Adapterklasse im Package org.xml.sax.ext genau für
    // diesen Zweck. Wir möchten keine Attribute angeben, aber wir müssen ein viertes
    // Argument an startElement(  ) übergeben.
    Attributes emptyAttributes = new AttributesImpl(  );

    // Wir verwenden keinen Namensraum für das Element
    startElement("", target, target, emptyAttributes);
    // Es konvertiert String-Daten in char-Arrays.
    char[] text = data.toCharArray(  );
    characters(text, 0, text.length);

    endElement("", target, target);

  }
}

Code-Beispiel: Ein SAX-Filter, der Verarbeitungsanweisungen in Elemente umwandelt

Wir haben diesen Filter bereits verwendet, um das Code-Beispiel Ein einfaches XML-Dokument ein Programm zu übergeben, das ein XML-Dokument an System.out ausgibt, und waren ein bisschen überrascht, dass das Folgende herauskam:

<xml-stylesheet>type="text/css" href="person.css"</xml-stylesheet>
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:vorname>Sydney</name:vorname>
    <name:nachname>Lee</name:nachname>
  </name:name>
  <auftrag project_id="p2"></auftrag>
</person>

Dieses Dokument ist nicht wohlgeformt! Problematisch ist hier, dass das Dokument zwei voneinander unabhängige Wurzelelemente enthält. Bei näherer Überlegung ist das auch gar nicht erstaunlich. Die Wohlgeformtheit wird normalerweise vom eingesetzten Parser überprüft, wenn er die Struktur des XML-Dokuments ausliest. SAX-Filter sollten den Client-Anwendungen wohlgeformte XML-Daten zur Verfügung stellen, das wird aber nicht zwingend von ihnen gefordert. Tatsächlich können sie sogar noch weitaus weniger wohlgeformte Daten als im vorliegenden Beispiel produzieren. Beispiele dafür sind das Einfügen von Start-Tags, die nicht geschlossen werden, die Ergänzung von Text durch unzulässige Zeichen wie Seitenvorschübe oder vertikale Einschübe und auch die Verwendung von in Namen nicht erlaubten Zeichen wie * und § in XML-Namen. Sie sollten nicht ohne weiteres annehmen, dass Daten, die Sie von einem XML-Filter bekommen, gültig oder wohlgeformt sind.

Wenn Sie eine Methode aufrufen möchten, ohne sie zu filtern, oder wenn Sie dieselbe Methode im zugrunde liegenden Handler ausführen möchten, können Sie dem Methodenaufruf als Präfix das Schlüsselwort super voranstellen. Dadurch wird die Variante der Methode aus der Superklasse aufgerufen. Standardmäßig übergibt jede Methode aus XMLFilterImpl lediglich die gleichen Argumente an die entsprechende Methode im Eltern-Handler. Das folgende Code-Beispiel zeigt das anhand eines Filters, der alle Zeichendaten in Großbuchstaben umwandelt, indem er die Methode characters( ) überschreibt.

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class UpperCaseFilter extends XMLFilterImpl {

  public void characters(char[] text, int start, int length)
   throws SAXException {

    String temp = new String(text, start, length);
    temp = temp.toUpperCase(  );
    text = temp.toCharArray(  );
    super.characters(text, 0, text.length);

  }

}

Code-Beispiel: Ein SAX-Filter, der Text in Großbuchstaben umwandelt

Die Anwendung eines Filters besteht aus den folgenden Schritten:

  1. Erzeugen eines Filter-Objekts, normalerweise durch Aufruf seines eigenen Konstruktors.
  2. Erzeugen des XMLReader, der das Dokument tatsächlich parsen wird, normalerweise durch Aufruf von XMLReaderFactory.createXMLReader( ).
  3. Registrieren des Filters beim Parser durch Aufruf der setParent( )-Methode des Filters.
  4. Einrichten eines ContentHandler im Filter.
  5. Parsen des Dokuments durch Aufruf der parse( )-Methode des Filters.

Die Details können von Anwendung zu Anwendung leicht voneinander abweichen. Zum Beispiel könnten Sie andere Handler als den ContentHandler einrichten oder den Eltern-Handler zwischen Dokumenten austauschen. In jedem Fall aber sollten Sie, wenn der Filter einmal am zugrundeliegenden XMLReader registriert worden ist, keine Methoden des zugrunde liegenden Parsers mehr direkt aufrufen. Mit dem Parser sollten Sie nur über den Filter sprechen. Das folgende Beispiel demonstriert, wie man den Filter aus dem vorherigen Code-Beispiel zum Parsen eines Dokuments verwenden kann:

XMLFilter filter = new UpperCaseFilter(  );
filter.setParent(XMLReaderFactory.createXMLReader(  ));
filter.setContentHandler(yourContentHandlerObject);
filter.parse(document);

Beachten Sie, dass die parse( )-Methode des Filters und nicht die zugrunde liegende parse( )-Methode des Parsers aufgerufen wird.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XML bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

  


Copyright © 2005 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XML in a Nutshell" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de