Java SAXパーサーの例

JavaのSAXパーサは、XMLドキュメントを解析するためのAPIを提供します。SAXパーサは、完全なXMLをメモリに読み込まず、XMLドキュメントを順次読み込むため、DOMパーサとは異なります。

SAXパーサ

javax.xml.parsers.SAXParserは、イベントハンドラを使用してXMLドキュメントを解析するメソッドを提供します。このクラスはXMLReaderインターフェースを実装し、parse()メソッドのオーバーロードバージョンを提供して、ファイル、InputStream、SAX InputSource、およびString URIからXMLドキュメントを読み取ります。実際の解析はHandlerクラスによって行われます。XMLドキュメントを解析するために独自のハンドラクラスを作成する必要があります。独自のハンドラクラスを作成するには、org.xml.sax.ContentHandlerインターフェースを実装する必要があります。このインターフェースには、イベントが発生したときに通知を受け取るコールバックメソッドが含まれています。たとえば、StartDocument、EndDocument、StartElement、EndElement、CharacterDataなどです。org.xml.sax.helpers.DefaultHandlerContentHandlerインターフェースのデフォルト実装を提供し、このクラスを拡張して独自のハンドラを作成できます。このクラスを拡張することをお勧めします。なぜなら、実装するメソッドがわずかしか必要ない場合があるためです。このクラスを拡張することで、コードをより清潔で保守可能に保つことができます。

SAXパーサーの例

さあ、SAXパーサーの例プログラムに移りましょう。後で詳細に異なる機能を説明します。employees.xml

<?xml version="1.0" encoding="UTF-8"?>
<Employees>
	<Employee id="1">
		<age>29</age>
		<name>Pankaj</name>
		<gender>Male</gender>
		<role>Java Developer</role>
	</Employee>
	<Employee id="2">
		<age>35</age>
		<name>Lisa</name>
		<gender>Female</gender>
		<role>CEO</role>
	</Employee>
	<Employee id="3">
		<age>40</age>
		<name>Tom</name>
		<gender>Male</gender>
		<role>Manager</role>
	</Employee>
	<Employee id="4">
		<age>25</age>
		<name>Meghna</name>
		<gender>Female</gender>
		<role>Manager</role>
	</Employee>
</Employees>

そこにXMLファイルがファイルシステムのどこかに保存されており、それを見ることで、それが従業員のリストを含んでいることがわかります。各従業員にはid属性とagenamegender、およびroleというフィールドがあります。私たちはこのXMLを解析し、Employeeオブジェクトのリストを作成するためにSAXパーサーを使用します。以下は、XMLのEmployee要素を表すEmployeeオブジェクトです。

package com.journaldev.xml;

public class Employee {
    private int id;
    private String name;
    private String gender;
    private int age;
    private String role;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
    
    @Override
    public String toString() {
        return "Employee:: ID="+this.id+" Name=" + this.name + " Age=" + this.age + " Gender=" + this.gender +
                " Role=" + this.role;
    }
    
}

独自のSAXパーサーハンドラクラスを作成し、DefaultHandlerクラスを拡張しましょう。


package com.journaldev.xml.sax;

import java.util.ArrayList;
import java.util.List;

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

import com.journaldev.xml.Employee;

public class MyHandler extends DefaultHandler {

	// Employeesオブジェクトを保持するリスト
	private List empList = null;
	private Employee emp = null;
	private StringBuilder data = null;

	// 従業員リストのgetterメソッド
	public List getEmpList() {
		return empList;
	}

	boolean bAge = false;
	boolean bName = false;
	boolean bGender = false;
	boolean bRole = false;

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

		if (qName.equalsIgnoreCase("Employee")) {
			// 新しいEmployeeを作成し、Mapに入れる
			String id = attributes.getValue("id");
			// Employeeオブジェクトを初期化し、id属性を設定
			emp = new Employee();
			emp.setId(Integer.parseInt(id));
			// リストを初期化
			if (empList == null)
				empList = new ArrayList<>();
		} else if (qName.equalsIgnoreCase("name")) {
			// フィールドのブール値を設定し、Employee変数の設定に使用されます
			bName = true;
		} else if (qName.equalsIgnoreCase("age")) {
			bAge = true;
		} else if (qName.equalsIgnoreCase("gender")) {
			bGender = true;
		} else if (qName.equalsIgnoreCase("role")) {
			bRole = true;
		}
		// データコンテナを作成
		data = new StringBuilder();
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if (bAge) {
			// age要素、Employeeのageを設定
			emp.setAge(Integer.parseInt(data.toString()));
			bAge = false;
		} else if (bName) {
			emp.setName(data.toString());
			bName = false;
		} else if (bRole) {
			emp.setRole(data.toString());
			bRole = false;
		} else if (bGender) {
			emp.setGender(data.toString());
			bGender = false;
		}
		
		if (qName.equalsIgnoreCase("Employee")) {
			// Employeeオブジェクトをリストに追加
			empList.add(emp);
		}
	}

	@Override
	public void characters(char ch[], int start, int length) throws SAXException {
		data.append(new String(ch, start, length));
	}
}

MyHandlerにはEmployeeオブジェクトのリストがフィールドとして含まれており、getterメソッドのみがあります。Employeeオブジェクトはイベントハンドラメソッドで追加されています。また、Employeeフィールドがあり、これを使用してEmployeeオブジェクトを作成し、すべてのフィールドが設定されたら、それを従業員リストに追加します。

オーバーライドするSAXパーサーメソッド

startElement()endElement()、およびcharacters()はオーバーライドする重要なメソッドです。SAXParserはドキュメントの解析を開始し、任意の開始要素が見つかるとstartElement()メソッドが呼び出されます。このメソッドをオーバーライドして、要素を識別するために使用されるブール変数を設定しています。また、このメソッドを使用して、Employee開始要素が見つかるたびに新しいEmployeeオブジェクトを作成しています。ここでid属性が読み取られ、Employeeオブジェクトのidフィールドが設定されます。characters()メソッドは、SAXParserが要素内で文字データを見つけたときに呼び出されます。SAXパーサーはデータを複数のチャンクに分割し、characters()メソッドを複数回呼び出す可能性があることに注意してください(ContentHandlerクラスのcharacters()メソッドのドキュメントを参照)。これが、このデータを保持するためにStringBuilderを使用し、append()メソッドを使用している理由です。endElement()は、StringBuilderデータを使用してemployeeオブジェクトのプロパティを設定し、Employeeオブジェクトを見つけたたびにリストに追加する場所です。上記のXMLをEmployeeオブジェクトのリストに解析するためにMyHandlerを使用するテストプログラムは以下のとおりです。

package com.journaldev.xml.sax;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

import com.journaldev.xml.Employee;

public class XMLParserSAX {

    public static void main(String[] args) {
    SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
    try {
        SAXParser saxParser = saxParserFactory.newSAXParser();
        MyHandler handler = new MyHandler();
        saxParser.parse(new File("/Users/pankaj/employees.xml"), handler);
        //従業員リストを取得
        List empList = handler.getEmpList();
        //従業員情報を出力
        for(Employee emp : empList)
            System.out.println(emp);
    } catch (ParserConfigurationException | SAXException | IOException e) {
        e.printStackTrace();
    }
    }

}

上記のプログラムの出力は次のとおりです。

Employee:: ID=1 Name=Pankaj Age=29 Gender=Male Role=Java Developer
Employee:: ID=2 Name=Lisa Age=35 Gender=Female Role=CEO
Employee:: ID=3 Name=Tom Age=40 Gender=Male Role=Manager
Employee:: ID=4 Name=Meghna Age=25 Gender=Female Role=Manager

SAXParserFactorySAXParserインスタンスを取得するためのファクトリメソッドを提供します。parseメソッドにはFileオブジェクトとMyHandlerインスタンスが渡され、コールバックイベントを処理するために使用されます。SAXParserは最初は少しわかりにくいかもしれませんが、大きなXMLドキュメントで作業している場合、DOM Parserよりも効率的なXMLの読み取り方法を提供します。これでJavaのSAX Parserに関するすべてです。

プロジェクトをGitHubリポジトリからダウンロードできます。

参考: SAXParser, DefaultHandler

Source:
https://www.digitalocean.com/community/tutorials/java-sax-parser-example