SAX

General
 About SAX
 Copyright Status
 Events vs. Trees
 FAQ
 Links

Java API
 Quickstart
 Features and Properties
 Filters
 Namespaces
 JavaDoc

SAX Evolution
 Genesis
 SAX 1.0 Overview
 SAX 2.0 Changes
 SAX 2.0 Extensions
 Other Languages

SourceForge Services
 Bugs/RFEs
 Project Page


SourceForge Logo

Filters

The SAX interface assumes two basic streams:

  • events flowing from the SAX driver (usually an XMLReader) to the application;
  • requests flowing from the application to the SAX driver.

With SAX1, programmers quickly realized that it was possible to use these streams in processing chains. Events (or requests) could flow through several different components, or "filters", and each filter could make changes to the stream as it passes through.

SAX2 formalizes this design technique by adding a new interface, org.xml.sax.XMLFilter, and a new helper class, org.xml.sax.helpers.XMLFilterImpl. These can be used to construct event processing pipelines, and can be used in two modes:

  • The chain can be used purely to postprocess SAX events, which might not be generated by an XMLReader;
  • Or the chain of filters can appear to be a single SAX XMLReader to the application, which processes both streams;

Long filter chains are not always the best approach, but you will find that it is sometimes easier to build complex XML applications if you can break them down into a collection of simple SAX components, each one a filter processing events from its parent.

org.xml.sax.XMLFilter Interface

The XMLFilter interface itself is very simple, extending the basic XMLReader interface with two additional methods:

public interface XMLFilter extends XMLReader
{
  public abstract void setParent (XMLReader parent);
  public abstract XMLReader getParent ();
}

In other words, a SAX2 XMLFilter is simply an XMLReader that has another XMLReader as its parent. It forwards most requests to that parent, and filters events reported by it, preprocessing them before they are reported to applications.

org.xml.sax.helpers.XMLFilterImpl Class

In one mode, a filter will implement not only the XMLFilter interface but also one or all of the various resolver and handler interfaces (EntityResolver, DTDHandler, ContentHandler, and ErrorHandler) as well as the extension interfaces (DeclHandler, LexicalHandler). To the parent XML reader, the filter is the client application receiving the events; to the client application, the filter is the SAX driver producing the events.

The XMLFilterImpl helper class provides a convenient base for deriving SAX2 filters. This class implements the XMLFilter, EntityResolver, DTDHandler, ContentHandler, and ErrorHandler interfaces. (The extension interfaces are not implemented here: DeclHandler and LexicalHandler). By default, it passes all events on unmodified, but the derived filter can override specific methods.

Example using XMLFilterImpl

Here's an example of a very simple filter that changes the Namespace URI http://www.foo.com/ns/ to http://www.bar.com/ wherever it appears in an element name (but not an attribute name, it's just an example):

public class FooFilter extends XMLFilterImpl
{
  public FooFilter ()
  {
  }

  public FooFilter (XMLReader parent)
  {
    super(parent);
  }


  /**
   * Filter the Namespace URI for start-element events.
   */
  public void
  startElement (String uri, String localName, String qName,
	Attributes atts)
  throws SAXException
  {
    if (uri.equals("http://www.foo.com/ns/")) {
      uri = "http://www.bar.com/ns/";
    }
    super.startElement(uri, localName, qName, atts);
  }


  /**
   * Filter the Namespace URI for end-element events.
   */
  public void
  endElement (String uri, String localName, String qName)
  throws SAXException
  {
    if (uri.equals("http://www.foo.com/ns/")) {
      uri = "http://www.bar.com/ns/";
    }
    super.endElement(uri, localName, qName);
  }

}

Note the use of super.startElement and super.endElement to send the event on to the client. In a real filter, it would be good to override the ContentHandler.startPrefixMapping and ContentHandler.endPrefixMapping methods as well as checking namespaces found in Attributes, so that the SAX event stream is internally consistent (well formed).