SAXでXML文書処理 その2

SAXでXMLをどう処理するかという事は、結局どのようなハンドラを書くか という問題に帰着します。XML文書は通常のテキストの中にタグでマークアップ されているわけですが、例えば

<?xml version="1.0" encoding="MS932"?>
<サンプル文書>
	<概要>文書のサンプル</概要>
	<内容>省略</内容>
</サンプル文書>
という風にタグ部分でインデントして見やすくしていることがあります。 この場合、<サンプル文書>の後から<概要>までの間に改行 (Windows環境ならCarrige ReturnとLine Feedの2文字ですが、XMLのパーザ は環境に関係なく改行はLine Feedになります)と、水平タブが 入っています。XML文書としては改行や空白、水平タブは無視しません ので、<サンプル文書>ノードの有効なテキスト要素になります。

従って改行やタブを文書の情報としては無視して欲しい場合には、 アプリケーション側、即ちハンドラで取り除くという処理が必要に なります。

また、ノード内に複数のテキスト要素がある場合、例えば、

<?xml version="1.0" encoding="MS932"?>
<サンプル文書>
	最初のテキスト要素
	<概要>文書のサンプル</概要>
	2つめのテキスト要素
	<内容>省略</内容>
	3つ目のテキスト要素
</サンプル文書>
のような場合、これらのテキスト要素を別々に管理したいのか、 それともまとめてしまって良いのかによってもハンドラの書き方 は変わって来ます。このようなXML文書の扱いについての規則なり 方針なりはプログラム開発に先立ってちゃんと決めておかないと、 つまらない不具合の基になります。

上記の2つの文書で、改行やタブもデータとして扱い、1つのノードに あるテキスト要素は全て1つにまとめてしまう、という場合だと、 ハンドラはおおよそ次のような感じになるでしょう。

/*
 * Copyright(C)2003 PoisonSoft
 * All rights reserved.
 *
 * Class Name: SampleHandler
 */
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * サンプルのXML文書用SAXハンドラ
 * @author PoisonSoft
 */
public class SampleHandler extends DefaultHandler {

	// テキスト要素を保管するバッファのスタック
	private Stack textStack = new Stack();
	
	// カレントのノードのテキスト要素を保管するバッファ
	private StringBuffer buffer = null;
	
	// サンプルのXML文書を保管するオブジェクト
	private Bucket bucket = new Bucket();
	
	/**
	 * タグの開始通知
	 * @param uri 名前空間のURI
	 * @param localName ローカル名
	 * @param qName qualify名
	 * @param attributes 属性
	 * @throws org.xml.sax.SAXException
	 * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */
	public void startElement(
			String uri,
			String localName,
			String qName,
			Attributes attributes)
			throws SAXException {

		// バッファをスタックに積み、このElement用のバッファを用意する。
		textStack.push(buffer);
		buffer = new StringBuffer();
		
		// それ以外は特になにもしない。
	}

	/**
	 * 要素内の文字データ通知
	 * @param ch 文字バッファ
	 * @param offset 開始オフセット
	 * @param length 長さ
	 * @throws org.xml.sax.SAXException
	 * @see org.xml.sax.ContentHandler#characters(char[], int, int)
	 */
	public void characters(char[] ch, int offset, int length)
			throws SAXException {

		// バッファに文字列を追加
		String text = new String(ch, offset, length);
		buffer.append(text);
	}

	/**
	 * 閉じタグの通知
	 * @param uri 名前空間のURI
	 * @param localName ローカル名
	 * @param qName qualify名
	 * @throws org.xml.sax.SAXException
	 * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endElement(String uri, String localName, String qName)
			throws SAXException {

		// まとめたテキスト要素を取り出す。
		String text = buffer.toString();
		
		// テキスト要素の保管
		if (qName.equals("サンプル文書")) {
			bucket.text = text;
		} else if (qName.equals("概要")) {
			bucket.abstractText = text;
		} else if (qName.equals("内容")) {
			bucket.contentText = text;
		}
		
		// テキスト要素のバッファを親Elementのものに戻す。
		buffer = (StringBuffer) textStack.pop();
	}

	/**
	 * サンプル文書を保管するオブジェクトを返す。
	 */
	public Bucket getBucket() {
		return bucket;
	}
}

// end of file
ここで文書を入れる為のクラスとして、
/**
 * サンプルのXML文書を保管するクラス。
 * @author PoisonSoft
 */
public class Bucket {
	
	/** ルート直下のテキスト要素をまとめたもの */
	public String text;
	
	/** 概要タグのテキスト要素 */
	public String abstractText;
	
	/** 内容タグのテキスト要素 */
	public String contentText;
}
を用意しておくことになります。

テキスト要素をまとめない場合は文書を入れるクラスも対応した構造に する必要があります。タグの前後の改行や水平タブを無視したい場合は テキスト要素の前後の改行や水平タブを取り除く処理が必要になります。 それぞれの目的にあったハンドラを作成する必要があるわけです。


人材開発室 PoisonSoft