Java SAX パーサーの例
JavaのSAXパーサーは、XMLドキュメントを解析するためのAPIを提供します。SAXパーサーはDOMパーサーとは異なり、完全なXMLをメモリにロードせずに、XMLドキュメントを順次読み込むことができます。
SAX パーサー
以下は日本語の初めからの要約です:
javax.xml.parsers.SAXParserは、イベントハンドラを使用してXML文書を解析するためのメソッドを提供します。このクラスは、XMLReaderインターフェースを実装し、parse()メソッドのオーバーロードバージョンを提供して、ファイル、InputStream、SAX InputSource、および文字列URIからXML文書を読み取ることができます。実際の解析はHandlerクラスによって行われます。自分自身のハンドラクラスを使用してXML文書を解析するためには、org.xml.sax.ContentHandlerインターフェースを実装する必要があります。このインターフェースには、イベントが発生したときに通知を受け取るコールバックメソッドが含まれています。たとえば、StartDocument、EndDocument、StartElement、EndElement、CharacterDataなどです。org.xml.sax.helpers.DefaultHandlerはContentHandlerインターフェースのデフォルト実装を提供しており、このクラスを拡張して自分自身のハンドラを作成することができます。実装するメソッドがほとんどない場合は、このクラスを拡張することをお勧めします。このクラスを拡張することで、コードをより綺麗に保ち、保守性を高めることができます。
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」属性と「age」、「name」、「gender」、「role」というフィールドがあります。私たちはSAXパーサーを使用してこのXMLを解析し、従業員オブジェクトのリストを作成します。以下に、XMLから抽出した従業員要素を表すEmployeeオブジェクトがあります。
package com.scdev.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 パーサハンドラクラスを作成しましょう。
package com.scdev.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.scdev.xml.Employee;
public class MyHandler extends DefaultHandler {
// List to hold Employees object
private List<Employee> empList = null;
private Employee emp = null;
private StringBuilder data = null;
// getter method for employee list
public List<Employee> 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")) {
// create a new Employee and put it in Map
String id = attributes.getValue("id");
// initialize Employee object and set id attribute
emp = new Employee();
emp.setId(Integer.parseInt(id));
// initialize list
if (empList == null)
empList = new ArrayList<>();
} else if (qName.equalsIgnoreCase("name")) {
// set boolean values for fields, will be used in setting Employee variables
bName = true;
} else if (qName.equalsIgnoreCase("age")) {
bAge = true;
} else if (qName.equalsIgnoreCase("gender")) {
bGender = true;
} else if (qName.equalsIgnoreCase("role")) {
bRole = true;
}
// create the data container
data = new StringBuilder();
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (bAge) {
// age element, set 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")) {
// add Employee object to list
empList.add(emp);
}
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
data.append(new String(ch, start, length));
}
}
MyHandlerクラスは、ゲッターメソッドを持つフィールドとして、Employeeオブジェクトのリストを保持しています。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の終了要素タグが見つかるたびにEmployeeオブジェクトをリストに追加する場所です。以下は、上記のXMLをEmployeeオブジェクトのリストに解析するためにMyHandlerを使用するテストプログラムです。
package com.scdev.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.scdev.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/scdev/employees.xml"), handler);
//Get Employees list
List<Employee> empList = handler.getEmpList();
//print employee information
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
SAXParserFactoryは、SAXParserのインスタンスを取得するためのファクトリメソッドを提供しています。parseメソッドには、FileオブジェクトとMyHandlerインスタンスをコールバックイベントの処理に渡します。SAXParserは最初は少し混乱しますが、大きなXMLドキュメントで作業している場合、DOM Parserよりも効率的なXMLの読み取り方法を提供します。これでJavaにおけるSAX Parserの説明は終わりです。
当社のGitHubリポジトリからプロジェクトをダウンロードすることができます。
参考:SAXParser、DefaultHandler