ファイヤープロジェクト
JavaでSAX
2003-07-20T15:13+09:00   matsu
JavaでSAXクラスライブラリを使用したプログラミングをしてみた.
SAXはSimple API for XMLの略である.その名のとおり(?),XMLプログラミングの際に嬉しいAPIである.JavaではSunがJAXP(Java API for XML Programing)というパッケージにSAXに対応したXMLパーサが入っている(このパッケージには,DOMに対応したXMLパーサも入っている).
SAXではXML文書を読み込んで,XML文書の先頭があった,タグがあった,文字列があった,などというイベントを検出し,イベントごとに設けられたメソッドを実行していくものである.文書を局所的にみて処理を行うので,パースにかかるメモリはDOMに比べて少なくて済む.その代わり,文書の局所しか見れないので,全体を見舞わした処理や前の要素に戻るという処理には向かない.
SAXのクラスライブラリを使うために.
apt-get install libxerces-java
export CLASSPATH=/usr/share/java/xerces.jar
これでjavax.xml.parsersをimportできる.ドキュメントによるとDOMのページで書いたlibcrimson-javaでもSAXができそうな雰囲気を感じた.apt-cache showによると,xercesはXMLスキーマ,DOM,SAXをサポートしているらしい.
パーサに書けるXML文書はとりあえず適当に.
<?xml version="1.0" encoding="Shift_JIS" ?>
<drinks>
    <drink type="soft">
        <name>Apple</name>
        <taste>Good</taste>
    </drink>

    <drink type="alcohol">
        <name>Beer</name>
	<taste>Bitter</taste>
    </drink>

    <drink type="alcohol">
        <name>Whiskey</name>
    </drink>
</drinks>
SAXはイベントドリブンである.Javaの場合イベントはパーサがXML文書を読み込んで行き,読み込んだ内容が以下だった場合に発生する.
  • XML文書の開始
  • XML文書の終了
  • 要素の開始
  • 要素の終了
  • 文字データ
  • 要素内の無視できる空白
  • 処理命令の通知
  • プレフィックスによる名前空間の範囲の開始
  • プレフィックスによる名前空間の範囲の終了
これらのイベントを処理するイベントハンドラクラスを作成する.
import org.xml.sax.*;
import org.xml.sax.helpers.*;

class SampleHandler extends DefaultHandler{
    private int tab=0;
    private String currentTag;

    /* XML文書の開始時の処理*/
    public void startDocument(){
	System.out.println("Here is start of XML document.");
    }

    /* 要素の開始時の処理*/
    public void startElement(String namespaceURI,String localName,String qName,Attributes attrs){
	tabbing();
	System.out.println("Start Element");
	/* namespaceURIの表示 要調査*/
	if(namespaceURI!=""){
	    tabbing();
	    System.out.println("namespaceURI= "+namespaceURI);
	}
	/* localNameの表示 要調査*/
	if(localName!=""){
	    tabbing();
	    System.out.println("localName= "+localName);
	}
	/*タグ名を表示*/
	if(qName!=""){
	    tabbing();
	    System.out.println("qName= "+qName);
	    currentTag=qName;
	}
	/* 属性の表示*/
	/* 属性の数はAttributesのgetLength()で取得*/
	if(attrs.getLength()!=0){
	    tabbing();
	    /* 属性の値はAttributesのgetValue(int)で取得*/
	    System.out.println("attrs= "+attrs.getValue(0));
	    }
	tab++;
    }

    /*要素の終了時の処理*/
    public void endElement(String namespaceURI,String localName,String qName){
	tab--;	
	tabbing();
	System.out.println("End element.");
	if(namespaceURI!=""){
	    tabbing();
	    System.out.println("namespaceURI= "+namespaceURI);
	}
	if(localName!=""){
	    tabbing();
	    System.out.println("localName= "+localName);
	}

	if(qName!=""){
	    tabbing();
	    System.out.println("qName= "+qName);
	}
	currentTag="";
    }

    /* XML文書の終了時の処理*/
    public void endDocument(){
	System.out.println("Here is end of XML document.");
    }

    /* 文字列の処理*/
    public void characters(char[] ch,int start,int length){
	if(currentTag.equals("name")||currentTag.equals("taste")){
	    String str=new String(ch,start,length);
	    if(str.length()!=0){
		tab++;
		tabbing();
		tab--;
		System.out.println("====="+str+"=====");
	    }
	}
    }

    /* 処理命令を受けた時の処理 要調査*/
    public void processingInstruction(String target,String data){}

    /* プレフィックスによる名前空間の範囲の開始時 要調査*/
    public void startPrefixMapping(String prefix,String uri){}

    /* プレフィックスによる名前空間の範囲の終了時 要調査*/
    public void endPrefixMapping(String prefix){}

    /* インデント用.これはこのクラス独自のメソッド*/
    private void tabbing(){
	for(int i=0;i<tab;i++){
	    System.out.print("\t");
	}
    }
}
あとは,このクラスをパーサに渡すだけである.パーサはイベントが発生すると,イベントハンドラクラスの対応するメソッドを呼び出す.
import java.io.*;
import javax.xml.parsers.*;

class SaxSample{
    public static void main(String args[]){
	try{
	    /*パーサのFactoryを作成
	      名前からしてFactoryパターンで,Singletonだと思う.*/
	    SAXParserFactory spf=SAXParserFactory.newInstance();
	    /*パーサを取得*/
	    SAXParser sp=spf.newSAXParser();
	    
	    /*イベントハンドラを作成*/
	    SampleHandler sh=new SampleHandler();

	    /*イベントハンドラに入力データとイベントハンドラを渡す*/
	    sp.parse(new FileInputStream("sample.xml"),sh);
	}catch(Exception e){
	    e.printStackTrace();
	}
    }
}
これら二つのjavaファイルをコンパイル.java SaxSampleすると以下が出力される.ちなみに私はCLASSPATHに.をいれるのを忘れてハマッた.
Here is start of XML document.
Start Element
qName= drinks
        Start Element
        qName= drink
        attrs= soft
                Start Element
                qName= name
                                =====Apple=====
                End element.
                qName= name
                Start Element
                qName= taste
                                =====Good=====
                End element.
                qName= taste
        End element.
        qName= drink
        Start Element
        qName= drink
        attrs= alcohol
                Start Element
                qName= name
                                =====Beer=====
                End element.
                qName= name
                Start Element
                qName= taste
                                =====Bitter=====
                End element.
                qName= taste
        End element.
        qName= drink
        Start Element
        qName= drink
        attrs= alcohol
                Start Element
                qName= name
                                =====Whiskey=====
                End element.
                qName= name
        End element.
        qName= drink
End element.
qName= drinks
Here is end of XML document.
とまあ,こんな感じでXML文書を処理して行く.
このプログラムではXML文書が妥当かどうかのチェックは行っていない.XML文書が妥当かどうかはより上位の問題(たとえば人間がXML文書を記述する際の問題)であって,パースの際の問題ではない,らしい.ので,XML文書が妥当かどうかは別の機構でチェックすればよい.
別の表現をすると,妥当かどうかはXML文書を書く際の問題であって,読む際には当然妥当である,ということだろうか.
matsu(C)
Since 2002
Mail to matsu