import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
public class Main {
public static void main(String args) {
PrintStream ps = createPrintStream("MusicLibrary_Copy.xml");
XMLReader xr = createXMLReader(ps);
try{
xr.parse(new InputSource(new FileReader("MusicLibrary.xml")));
}
catch (Exception e){
e.printStackTrace();
}
ps.close();
}
public static XMLReader createXMLReader(PrintStream ps){
try{
XMLReader xr=XMLReaderFactory.createXMLReader();
xr.setContentHandler(new Main().new DefaultHandlerImpl(ps));
return xr;
}
catch (Exception e){
return null;
}
}
public static PrintStream createPrintStream(String fileName){
OutputStream os;
try{
os =new FileOutputStream(fileName);
PrintStream ps;
try{
ps = new PrintStream(os,true,"UTF-8");
return ps;
} catch (Exception e){
e.printStackTrace();
}
} catch (FileNotFoundException e){
e.printStackTrace();
}
return null;
}
public class DefaultHandlerImpl extends DefaultHandler{
public DefaultHandlerImpl(PrintStream ps){
this.ps=ps;
}
private PrintStream ps;
public void startElement(String namespaceURI,
String localName,
String qName,
Attributes attr) throws SAXException{
ps.print("<" + localName);
for (int i=0; i < attr.getLength(); i++){
ps.print(" "+attr.getLocalName(i)+"=\""+attr.getValue(i)+"\"");
}
ps.print(">");
}
public void endElement(String namespaceURI,
String localName,
String qName) throws SAXException{
ps.print("</" + localName + ">");
}
public void characters(char ch,
int start,
int length) throws SAXException{
for (int i=0;i<length;i++){
char outChar=ch[start + i];
ps.print(outChar);
}
}
}
}
このプログラムではXMLReaderのインスタンスxrを生成してコンテンツを処理するDefaultHandlerImplクラスのインスタンスを登録し、xr.parseで一気に読み込みます.あとは、コンテンツが処理されるたびにDefaultHandlerImplクラスのメソッドがコールバックで起動されるという筋書きです.
簡単にするために、各メソッドでは
1.要素の始まりでは"<"+要素名+属性値+">"をPrintStreamに書き出します.
2.要素間のテキストはそのまま内容をPrintStreamに書き出します.
3.要素の終了では"</"+要素名+">"をPrintStreamに書き出します.
という処理を直にやっています.
構造からご覧になってわかると思いますが、
1.SAXはいったん走り出したら途中で止めるということができません.(例外でも投げれば止められるでしょうか、キレイではありません)
2.イベント処理です.つまりイベントを受け取った時点で処理できなければ、自分で記憶させる必要があります.SAX側はイベントを発生させてくれますが、そのあとの面倒まで見てくれません.例えばpart/title,chapter/title、section/titleで処理を切り分けたければ、親がどの要素であるかを記憶しておかねばなりません.
という感じです.もしXMLのフィルターのようなプログラムを作るのだったら、ほぼ出力も入力と同じ構造をしており、またあまり深い階層にならないものならSAXでも作れるでしょう.もしそうでなければ、SAXで読んで自分でメモリー上にJavaのクラス(のインスタンス)に展開してやらないと無理です.
SAXはDITA Open Toolkitでも使われています.DITAのテーブルはCALSのテーブルですが、入力にentry/@colnameがなくても、このSAXでの読み込みでちゃんとつけてくれます.DocBookはそのような仕組みがないので、スタイルシートで自前で@colnameを判定する(つうまり自分が何カラム目か判定する)処理をやっています.
SAXはPUSH型のパーサーと言われます.パーサーからハンドラーの側にどんどんイベント(コンテンツ)がPUSHされるからです.次はPULL型の処理を見てみたいと思います.