トップJava > SAXによるXML文書の操作

SAXによるXML文書の操作

SAXとは

SAXは DOMと並んでXML文書の操作に広く使われるAPIである[1]。 DOMの場合、XML文書の階層構造をメモリ上で構築するのに適している。 全体をメモリに読み込めないほど巨大なXML文書では使用できない。

一方、SAXはXML文書ファイルをシリアルに読み込んで、 タグの始まりや終わりを検出するごとに、処理ルーチンを呼び出すものである。 XML文書の階層は一切意識していない。 それはユーザプログラムが行わなければならない。 処理が単純ゆえに、DOMに比べて高速であるという特徴がある。

OSM(OpenStreetMap)のように、巨大な XMLファイルの処理に適している。 ただし、巨大な XMLファイルからごく一部のデータだけを抽出する場合、 SAXでも高速であるとは言い切れない。

行毎にデータを読み込み、頭の数個の文字をチェックするだけで、 その行が必要か不要かを判断できる場合には、SAXも使わず、自前のプログラムでパースする方が明らかに 高速処理が可能である。

概ねほぼ全ての行をパースしなければならないような場合は SAX を使う方がいいであろう。

OSM XMLファイルのパース

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);
    }
}

リファレンス

[1] SAXによるXML文書の操作
[2] SAXでのXML読み込みプログラム
[3] Using SAXparser to get info to more than one element (Android)