SAXは DOMと並んでXML文書の操作に広く使われるAPIである[1]。 DOMの場合、XML文書の階層構造をメモリ上で構築するのに適している。 全体をメモリに読み込めないほど巨大なXML文書では使用できない。
一方、SAXはXML文書ファイルをシリアルに読み込んで、 タグの始まりや終わりを検出するごとに、処理ルーチンを呼び出すものである。 XML文書の階層は一切意識していない。 それはユーザプログラムが行わなければならない。 処理が単純ゆえに、DOMに比べて高速であるという特徴がある。
OSM(OpenStreetMap)のように、巨大な XMLファイルの処理に適している。 ただし、巨大な XMLファイルからごく一部のデータだけを抽出する場合、 SAXでも高速であるとは言い切れない。
行毎にデータを読み込み、頭の数個の文字をチェックするだけで、 その行が必要か不要かを判断できる場合には、SAXも使わず、自前のプログラムでパースする方が明らかに 高速処理が可能である。
概ねほぼ全ての行をパースしなければならないような場合は SAX を使う方がいいであろう。
OSMファイルは大きくは node、way、relation セクションからなる。
例えば、node オブジェクトは <node id=... /> のように1行だけで完結することが多いが、 <node id=... > で始まり、途中の < tag k=... v=.../> が1行以上続き、 </node> で終わることもある。
前者は /> が現われた段階で node に対する endElementがコールされる。後者の場合には、 </node>が検出された段階で node に対する endElementがコールされる。
SAXによる OSM XMLファイルのパースは以下のようになる。 tag、nd、member オブジェクトはそれぞれ1行で完結しているため、開始処理で全ての処理が終わるため、 endElement では何もする必要はない。
node、way、relation オブジェクトは終了時には tag の処理が必要である。 way はそのほかに nd の処理が必要であり、relation の場合は member の処理が必要となる。
開始処理や終了処理の具体内容は別のページで述べる。
import java.nio.file.Paths; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; public class Parser extends DefaultHandler { public void startElement (String namespaceURI, String localName, String qName, Attributes atts ) { if (qName.equals("node")) { // nodeオブジェクトの開始処理 } else if (qName.equals("way")) { // wayオブジェクトの開始処理 } else if (qName.equals("relation")) { // relationオブジェクトの開始処理 } else if (qName.equals("tag")) { // tagオブジェクトの開始処理 … node, way, relationの子供 } else if (qName.equals("nd")) { // ndオブジェクトの開始処理 … wayオブジェクトの子供 } else if (qName.equals("member")) { // memberオブジェクトの開始処理 … relationオブジェクトの子供 } } public void endElement(String namespaceURI, String localName, String qName) { if (qName.equals("node")) { // nodeオブジェクトの終了処理 } else if (qName.equals("way")) { // wayオブジェクトの終了処理 } else if (qName.equals("relation")) { // relationオブジェクトの終了処理 } } public static void main( String[] args ) throws Exception { long start = System.currentTimeMillis(); String src = args.length >= 1 ? args[0] : "c:/osm/japan.osm"; SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); Parser handler = new Parser(); parser.parse(Paths.get(src).toFile(), handler); System.out.printf("実行時間: %.2f分\n", (System.currentTimeMillis()-start)/60000.0); } }