このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
DOM は W3C が主導で規格化されました。 一方、SAX(Simple API for XML) は XML-DEV メーリングリストの有志によっ て開発されました。 これは、非常に単純な XML の API です。 パーサにハンドラ(オブザーバ)を与えておくことで、要素に遭遇したり、エラー が発生した場合にハンドラのメソッドを呼び出すというものです。
SAX は DOM のように XML を修正したり生成したりできず、読み込みのみです。 また、 DOM はドキュメントがメモリ上に配置され、プログラムがそれを探索 したりできますが、 SAX は XML 文書を読み込むときに順に様々なハンドラの メソッドが呼び出されるという、イベントドリブンなプログラミ ング手法になります。 但し、このような特徴から非常に軽量で高速でメモリも消費しません。 そのため、単なる読み込み、集計、オブジェクトの作成など、特定の用途には DOM より向いています。
javax.xml.parsers.SAXParser オブジェクトは、 parse メソッドで XML の構文解析を行います。 引数は入力と、 DefaultHandler の二つを取ります。 入力は、 java.io.File, org.xml.sax.InputSource, java.io.InputStream の他に、 URI を文字列で指定することもできます。
このオブジェクトを作るには、 javax.xml.parser.SAXParserFactory のファクトリメソッド newInstance() を呼び出し、さらに作られたオブジェクトに対して、newSAXParser メソッド を呼び出します。
org.xml.sax.helpers.DefaultHandler は org.xml.sax.ContentHandler の他、, DTDHandler, EntityResolver, ErrorHandler の各インターフェイスを実装したスケルトンクラスです。 なお、 org.xml.sax.ContentHandler は java.net.ContentHandler とインタ フェース名が一致するため、次のような import 文で次の表現を避ける必要が あります。
import java.net.*;
import org.xml.sax.*;
class X implements ContentHandler {
...
DefaultHandler はデフォルトコンストラクタを持ちます。 もちろんこれをそのまま parser に与えて動かしても何もしません。 但し、この時も parser が構文解釈中に以下のメソッドを呼び出します。 また、XML文書の構文が誤っている時は例外が発生します。
この、構文解釈により何らかの出力を得たい場合は、 DefaultHandler を継承したクラスを作り、下記のメソッドをオーバライドします。 そして、インスタンス化して parser に与えます。
なお、startElement の引数の Attributes は DOM のオブジェクトではなく、 org.xml.sax.Attributes 型です。 下記にメソッドを示します。
単純に要素の開始時に、与えられる情報を表示するだけのプログラムを示しま す。
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
class TestHandler extends DefaultHandler {
public TestHandler(){
super();
}
@Override
public void startElement(String uri, String localname,
String qname, Attributes attributes){
System.out.println("要素");
System.out.println("URI:"+uri);
System.out.println("LocalName:"+localname);
System.out.println("QName"+qname);
for(int i=0; i<attributes.getLength(); i++){
System.out.println(" 属性"+i);
System.out.println(" LocalName:"+attributes.getLocalName(i));
System.out.println(" QName:"+attributes.getQName(i));
System.out.println(" Type:"+attributes.getType(i));
System.out.println(" Value:"+attributes.getValue(i));
}
}
}
class Rei {
private static SAXParser getSAXParser() throws Exception {
final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
return parserFactory.newSAXParser();
}
public static void main(String[] arg) throws Exception {
final SAXParser parser = getSAXParser();
parser.parse(new FileInputStream("itemlist.xml"), new TestHandler());
}
}
次に次のような XML 文書を考えます。
<?xml version="1.0" encoding="Shift_JIS" standalone="no"?> <itemlist> <item> <data name="品名" value="りんご"/> <data name="単価" value="200"/> <data name="個数" value="3"/> <data name="合計" value="600"/> </item> <item> <data name="品名" value="みかん"/> <data name="単価" value="100"/> <data name="個数" value="5"/> <data name="合計" value="500"/> </item> <item> <data name="品名" value="もも"/> <data name="単価" value="300"/> <data name="個数" value="1"/> <data name="合計" value="300"/> </item> </itemlist>
これに対して、<data name="合計" value="xxx"/> という要素のみを取 り出し、合計の値を合算して合計値を計算するプログラムを示します。
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
class ExHandler extends DefaultHandler {
private int total;
public ExHandler(){
super();
total=0;
}
public int getTotal(){
return total;
}
@Override
public void startElement(String uri, String localname,
String qname, Attributes attributes){
if(!localname.equals("data")) return;
if(!attributes.getValue("name").equals("合計")) return;
total += Integer.parseInt(attributes.getValue("value"));
}
}
class Rei {
private static SAXParser getSAXParser() throws Exception {
final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
return parserFactory.newSAXParser();
}
public static void main(String[] arg) throws Exception {
final SAXParser parser = getSAXParser();
final ExHandler handler = new ExHandler();
parser.parse(new FileInputStream("itemlist.xml"), handler);
System.out.println(handler.getTotal());
}
}
次に、 XML の入力にしたがって、オブジェクトを作成します。
まず、作成するオブジェクトとして、コンストラクタ、各値の setter、 toString を与えたクラスを示します。
class Item {
public Item(){
}
private String name;
private int price;
private int number;
private int total;
public void setName(String name){
this.name = name;
}
public void setPrice(int price){
this.price = price;
}
public void setNumber(int number){
this.number = number;
}
public void setTotal(int total){
this.total = total;
}
@Override
public String toString(){
return name+": 単価 "+price+", 個数 "+number+", 合計 "+total;
}
}
次に、 main を先に示します。 これは java.util.LinkedList のオブジェクトを作り、 ExHandler のコンス トラクタに与えています。 そして、この ExHandler が XML を読みながらオブジェクトのリストを作成し ます。 読み終えたら各要素を表示します。
import java.io.*;
import javax.xml.parsers.*;
import java.util.*;
import org.xml.sax.*;
class Rei {
private static SAXParser getSAXParser() throws Exception {
final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
return parserFactory.newSAXParser();
}
public static void main(String[] arg) throws Exception {
final SAXParser parser = getSAXParser();
final LinkedList<Item> list = new LinkedList<Item>();
final ExHandler handler = new ExHandler(list);
parser.parse(new FileInputStream("itemlist.xml"), handler);
for(Item i: list){
System.out.println(i);
}
}
}
ExHandler では startElement で次の処理をします。
import java.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
class ExHandler extends DefaultHandler {
private List<Item> list;
public ExHandler(List<Item> list){
super();
this.list = list;
}
public List<Item> getList(){
return list;
}
private Item item=null;
@Override
public void startElement(String uri, String localname,
String qname, Attributes attributes){
if(localname.equals("item")){
item = new Item();
return;
}
if(!localname.equals("data")) return;
if(attributes.getValue("name").equals("品名")){
item.setName(attributes.getValue("value"));
return;
}
if(attributes.getValue("name").equals("単価")){
item.setPrice(Integer.parseInt(attributes.getValue("value")));
return;
}
if(attributes.getValue("name").equals("個数")){
item.setNumber(Integer.parseInt(attributes.getValue("value")));
return;
}
if(attributes.getValue("name").equals("合計")){
item.setTotal(Integer.parseInt(attributes.getValue("value")));
return;
}
}
@Override
public void endElement(String uri, String localname,
String qname){
if(!localname.equals("item")) return;
list.add(item);
}
}
SAX パーサは整形式のチェックだけではなく、 DTD や XML Schema に対する 妥当性もチェックできます。
DTD のチェックをするには javax.xml.parser.SAXParserFactory オブジェク トに対して setValidating メソッドで true を指定すると構文チェックしま す。 この時、妥当性に問題がある場合は、与える DefaultHandler の error メソッ ドが呼び出されます。 そのため、実際に DefaultHandler を継承したクラスでは error メソッドを継承し、 与えられた例外に対して、適切な処理を行います。 以下が構文チェックをする最小限のプログラムです。
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.validation.*;
class TestHandler extends DefaultHandler {
public TestHandler(){
super();
}
@Override
public void error(SAXParseException e) throws SAXException {
System.out.println(e);
}
}
class Rei {
private static SAXParser getSAXParserWDTD() throws Exception {
final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setValidating(true);
parserFactory.setNamespaceAware(true);
return parserFactory.newSAXParser();
}
public static void main(String[] arg) throws Exception {
final SAXParser parser = getSAXParserWDTD();
parser.parse(new FileInputStream("sample.xml"), new TestHandler());
}
}
これに対して下記のような XML 文書が妥当性の検査に成功します。
<?xml version="1.0" encoding="Shift_JIS" standalone="yes" ?> <!DOCTYPE class [ <!ELEMENT class (teacher+,time+)> <!ATTLIST class name CDATA #REQUIRED> <!ELEMENT teacher EMPTY> <!ATTLIST teacher name CDATA #REQUIRED> <!ELEMENT time EMPTY> <!ATTLIST time day (mon|tue|wed|thu|fri|sat|sun) #REQUIRED period CDATA #REQUIRED> ]> <class name="データ構造とアルゴリズム II" > <teacher name="坂本直志" /> <time day="tue" period="2" /> </class>
<!ELEMENT class (teacher+,time+)> <!ATTLIST class name CDATA #REQUIRED> <!ELEMENT teacher EMPTY> <!ATTLIST teacher name CDATA #REQUIRED> <!ELEMENT time EMPTY> <!ATTLIST time day (mon|tue|wed|thu|fri|sat|sun) #REQUIRED period CDATA #REQUIRED>
<?xml version="1.0" encoding="shift_jis" standalone="no" ?> <!DOCTYPE class SYSTEM "class.dtd" > <class name="データ構造とアルゴリズム II" > <teacher name="坂本直志" /> <time day="tue" period="2" /> </class>
XML Schema のチェックをするには javax.xml.parser.SAXParserFactory オブジェク トに対して setValidating メソッドに false を指定します。 次に、 javax.xml.validator.SchemaFactory で XMLSchema の検証器 (Validator)を作成し、SAXParserFactory オブジェクトに setSchema で与え ます。 Validator を作るには newInstance スタティックメソッドの引数に XMLConstants.W3C_XML_SCHEMA_NS_URI を与えます。
以下が構文チェックをする最小限のプログラムです。
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.validation.*;
class TestHandler extends DefaultHandler {
public TestHandler(){
super();
}
@Override
public void error(SAXParseException e) throws SAXException {
System.out.println(e);
}
}
class Rei {
private static SAXParser getSAXParserWSchema() throws Exception {
final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setValidating(false);
parserFactory.setNamespaceAware(true);
final SchemaFactory sf = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
parserFactory.setSchema(sf.newSchema());
return parserFactory.newSAXParser();
}
public static void main(String[] arg) throws Exception {
final SAXParser parser = getSAXParserWSchema();
parser.parse(new FileInputStream("sample.xml"), new TestHandler());
}
}
これに対して以下の XML 文書は妥当性チェックに成功します。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="class" type="classType" /> <xsd:complexType name="classType"> <xsd:sequence> <xsd:element name="teacher" type="teacherType" /> <xsd:element name="time" type="timeType" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string"/> </xsd:complexType> <xsd:complexType name="teacherType"> <xsd:attribute name="name" type="xsd:string"/> </xsd:complexType> <xsd:complexType name="timeType"> <xsd:attribute name="day" type="dayType"/> <xsd:attribute name="period" type="xsd:int"/> </xsd:complexType> <xsd:simpleType name="dayType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="mon"/> <xsd:enumeration value="tue"/> <xsd:enumeration value="wed"/> <xsd:enumeration value="thu"/> <xsd:enumeration value="fri"/> <xsd:enumeration value="sat"/> <xsd:enumeration value="sun"/> </xsd:restriction> </xsd:simpleType> </xsd:schema>
<?xml version="1.0" encoding="shift_jis" standalone="no" ?> <class name="データ構造とアルゴリズム II" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="class.xsd" > <teacher name="坂本直志" /> <time day="tue" period="2" /> </class>