revised: Oct./26th/03
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の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()によって生成します。このとき実装は、次の順番で選択されます。
createXMLReader(String className)の引数org.xml.sax.driverの値META-INF/services/org.xml.sax.driverクラス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>
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)と同様の結果が得られることが確認できます。