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)と同様の結果が得られることが確認できます。