SAX - Simple API for XML

revised: Oct./26th/03

SAXの概要

SAX(Simple API for XML)もDOMと同様の働きをするXMLパーサ用のAPIです。

SAXは、メーリングリストXML-DEVのコラボレーションにより誕生し、国際的な標準化団体による仕様ではないにも関わらず、デファクト・スタンダード(事実上の標準、業界標準)となっているAPIです。Java用のイベント駆動型の軽量なAPIであり、ランダムアクセスや構造の変更といった要件には弱いものの、逐次的な処理が可能であれば、メモリ消費量、実行速度の点で優れています。もともと、Java用のXML APIであり、DOMと比べてJavaとの相性が良いといえます。

DOMの場合は、解析した結果を静的な木構造で返しました。要素や内容は、DOMツリーの各ノードとして扱われます。一方、SAXは、イベント駆動型のAPIで、解析結果により、文書の開始、要素の開始などのイベントを生成し、アプリケーションはイベントを受け取るハンドラによって対応する処理を実施します。

一方、SAXはメモリの消費量が少なく高速です。DOMは静的な構造を保持するので、構造に対する変更や複数文書間をまたぐ操作などで便利ですが、DOMツリーを静的にメモリ上に保持するため、巨大なXML文書の場合はメモリを圧迫しかねません。SAXは、巨大なXML文書に対する処理や、構造を変えない場合などに便利です。

SAXによる妥当性検証

イベント駆動型といっても、イメージがわきずらい思いますが、実際にSAXのAPIを使って解析してみるとよく分かります。SAXでは、XML文書の先頭から解析をはじめ、文書の開始、要素の開始などのイベントを受け取って、アプリケーション側が動作を指定します。このとき、イベントを受け取るクラスをイベント・ハンドラと呼びます。DOMでも、ErrorHandlerというハンドラを定義しましたが、SAXでは、ErrorHandlerのほかに、ContentHandler, DTDHandlerと言ったインタフェースを実装します。

リスト1は、「JAXPによるDOM解析」で紹介した、妥当性検証を行うDOMパーサであるリスト10をSAXを用いて実現するものです。

クラスSAXParseDemoはSAXパーサを使うメインのクラスです。SAXパーサには、インタフェースorg.xml.sax.XMLReaderを介してアクセスします。その実装クラスを直接newするのではなく、ファクトリー・クラスorg.xml.sax.helpers.XMLReaderFactoryのファクトリー・メソッドcreateXMLReader()によって生成します。このとき実装は、次の順番で選択されます。

  1. createXMLReader(String className)の引数
  2. システム・プロパティorg.xml.sax.driverの値
  3. JARファイルのMETA-INF/services/org.xml.sax.driver
  4. システム・デフォルト

クラスMySAXHandlerは、ヘルパー・クラスorg.xml.sax.helpers.DefaultHandlerを継承しており、イベントを受け取ったときの動作を指定します。ここでは、SAXParseDemoで生成したparserに、ContentHandler, DTDHandler, ErrorHandlerとして登録しています。

リスト1. SAXParseDemo.java

import org.w3c.dom.*;
import org.apache.xerces.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
class SAXParseDemo {
    public static void main(String[] args) {
        try {
            // SAXパーサの生成
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
            // フィーチャーの設定
            parser.setFeature("http://xml.org/sax/features/validation",true);
            parser.setFeature("http://apache.org/xml/features/validation/schema",true);
            parser.setFeature("http://xml.org/sax/features/namespaces",true);
            // ハンドラの生成
            DefaultHandler handler = new MySAXHandler();
            // ハンドラの登録
            parser.setContentHandler(handler);
            parser.setDTDHandler(handler);
            parser.setErrorHandler(handler);
            // 解析
            parser.parse(args[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MySAXHandler extends DefaultHandler {
    // ContentHandlerの実装
    public void startDocument() throws SAXException {
	    System.out.println("startDocument()");
    }
    public void endDocument() throws SAXException {
	    System.out.println("endDocument()");
    }
    public void startElement(java.lang.String uri,
                       java.lang.String localName,
                       java.lang.String qName,
                       Attributes atts)
                throws SAXException {
	    System.out.println("startElement()");
	    System.out.println("\tnamespace=" + uri);
	    System.out.println("\tlocal name=" + localName);
	    System.out.println("\tqualified name=" + qName);
	    for (int i = 0; i < atts.getLength(); i++) {
		    System.out.println("\tattribute name=" + atts.getLocalName(i));
		    System.out.println("\tattribute qualified name=" + atts.getQName(i));
		    System.out.println("\tattribute value=" + atts.getValue(i));
		}
    }
    public void endElement(java.lang.String uri,
                       java.lang.String localName,
                       java.lang.String qName)
                throws SAXException {
        System.out.println("endElement()");
    }
    public void characters(char[] ch,
                       int start,
                       int length)
                throws SAXException {
        System.out.println("characters()" + new String(ch, start, length));
    }
    // ErrorHandlerの実装
    public void warning(SAXParseException e) {
        System.out.println("警告: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
    public void error(SAXParseException e) {
        System.out.println("エラー: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
    public void fatalError(SAXParseException e) {
        System.out.println("深刻なエラー: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
}

DOMアプリケーションリスト10 (DomParseDemo4.java)と同様に、リスト1 (SAXParseDemo.java)で、リスト11 (demo6.xml)を処理すると、リスト2のようになります。

リスト2. SAXによる実行結果

>javac SAXParseDemo.java
>java SAXParseDemo demo6.xml
startDocument()
startElement()
        namespace=urn:address
        local name=address
        qualified name=hlq:address
        attribute name=schemaLocation
        attribute qualified name=xsi:schemaLocation
        attribute value=urn:address address.xsd
characters()
エラー: 5行目
cvc-enumeration-valid: Value 'man' is not facet-valid with respect to enumeratio
n '[male, female]'. It must be a value from the enumeration.
エラー: 5行目
cvc-attribute.3: The value 'man' of attribute 'sex' on element 'hlq:item' is not
 valid with respect to its type, 'null'.
startElement()
        namespace=urn:address
        local name=item
        qualified name=hlq:item
        attribute name=sex
        attribute qualified name=sex
        attribute value=man
        attribute name=custid
        attribute qualified name=custid
        attribute value=E21099
characters()
startElement()
        namespace=urn:address
        local name=name
        qualified name=hlq:name
characters()菅井 学
endElement()
characters()
startElement()
        namespace=urn:address
        local name=access
        qualified name=hlq:access
        attribute name=kind
        attribute qualified name=kind
        attribute value=email
characters()
endElement()
characters()
...
C:\java\xml>

JAXPによるSAX解析

DOMの場合と同じく、SAXもJAXPで記述することもできます。リスト19は、リスト1のクラスSAXParseDemoを、JAXPのAPIで記述したものです。

リスト3. JAXPによるSAX (SAXParseDemo2.java)

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
class SAXParseDemo2 {
    public static void main(String[] args) {
        try {
            // SAXパーサのファクトリーの生成
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // フィーチャーの設定
            factory.setValidating(true);
            factory.setNamespaceAware(true);
            factory.setFeature("http://apache.org/xml/features/validation/schema",true);
            // SAXパーサの生成
            SAXParser parser = factory.newSAXParser();
            // ハンドラの生成
            DefaultHandler handler = new MySAXHandler();
            // 解析
            parser.parse(args[0],handler);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

リスト3をコンパイルして demo6.xml を解析すると、リスト1の実行結果(リスト2)と同様の結果が得られることが確認できます。



Copyright © 2003 SUGAI, Manabu. All Rights Reserved.