JavaでSAXクラスライブラリを使用したプログラミングをしてみた.
SAXとは
環境の準備
早速試す
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文書を書く際の問題であって,読む際には当然妥当である,ということだろうか.