この記事から得られる知識
- `javax.xml`パッケージの全体像と、Javaのバージョンアップに伴う歴史的背景
- 主要なXML解析APIであるDOM、SAX、StAXの概念、それぞれの長所と短所、そして具体的な使い方
- XMLデータとJavaオブジェクトをマッピングするJAXBの基本的な使い方とマーシャリング・アンマーシャリングの実装方法
- Java 9以降のモジュールシステム導入やJava 11での標準ライブラリからの削除といった重要な変更点と、それに対応するための具体的な設定方法
- 各APIの特性を理解し、プロジェクトの要件に応じて最適な技術を選定するための判断基準
はじめに: JavaとXMLの深い関係
Javaは、その誕生以来、エンタープライズシステム開発の中心的な役割を担ってきました。そして、その歴史の中で、XML (Extensible Markup Language) は設定ファイル、データ交換フォーマット、Webサービス通信など、様々な場面で利用されてきました。このJavaとXMLの橋渡し役として長年活躍してきたのが、`javax.xml`パッケージです。
`javax.xml`は、Javaプラットフォームに標準で搭載されてきたAPI群であり、開発者はこれを利用することで、特定のXMLパーサー実装に依存しない、ポータブルなXML処理プログラムを記述することができました。しかし、Javaの進化とともに`javax.xml`の立ち位置も大きく変化しています。特にJava 9で導入されたモジュールシステム、そしてJava 11での標準ライブラリからの削除は、現代のJava開発者にとって必ず理解しておくべき重要な変更点です。
本記事では、`javax.xml`パッケージに含まれる主要なAPIの詳細な使い方から、Javaのバージョンアップに伴う変更点の歴史、そして最新のJava環境でこれらのAPIをどのように扱うべきかまで、網羅的に詳しく解説していきます。
第1章: javax.xml と JAXP の全体像
`javax.xml`パッケージ群の中心的な存在が JAXP (Java API for XML Processing) です。 JAXPは、XMLドキュメントの解析、検証、変換を行うための標準的な方法を提供します。 JAXPの最大の特徴はプラガビリティ(Pluggability)、つまり実装の差し替え可能性にあります。
これにより、アプリケーションのコードを変更することなく、使用するXMLパーサー(具体的な処理を行うライブラリ)を切り替えることが可能です。 例えば、Javaに標準でバンドルされているXercesベースのパーサーから、より高性能なサードパーティ製のパーサーに容易に変更できます。
JAXPは、主に以下の機能APIを提供しています。
API種別 | 名称 | 概要 |
---|---|---|
解析 (Parsing) | DOM (Document Object Model) | XMLドキュメント全体をメモリ上にツリー構造として展開し、ノードの追加や削除など自由な操作を可能にするAPIです。 |
SAX (Simple API for XML) | XMLドキュメントを先頭から順に読み進め、要素の開始や終了などのイベントを通知するイベント駆動型のAPIです。 | |
StAX (Streaming API for XML) | SAXと同様にストリームベースで処理しますが、アプリケーション側から能動的にデータを読みに行く「プル型」のAPIです。 | |
変換 (Transformation) | TrAX (Transformation API for XML) | XSLT (XML Stylesheet Language Transformations) を用いて、XMLドキュメントを別のXML、HTML、またはテキスト形式に変換するためのAPIです。 |
検証 (Validation) | Validation API | XML SchemaやDTD (Document Type Definition) を用いて、XMLドキュメントの構造が正しいかを検証するためのAPIです。 |
データバインディング | JAXB (Java Architecture for XML Binding) | XMLデータとJavaオブジェクトを相互に変換する機能を提供します。 これにより、XMLをJavaオブジェクトとして直感的に扱うことができます。 |
JavaバージョンとJAXP
JAXPはJavaのバージョンアップとともに進化してきました。例えば、Java SE 6にはJAXP 1.4が、Java SE 8にはJAXP 1.6が含まれていました。 しかし、前述の通り、Java 11で多くのJava EE関連モジュールと共に標準ライブラリから削除されました。 この歴史的経緯を理解することが、現代のJava開発では不可欠です。
第2章: DOMによるXML操作 – メモリ上のツリー構造
DOM (Document Object Model) は、XMLドキュメントをメモリ上にツリー構造として完全に読み込んでから操作するAPIです。 HTML/XMLドキュメントの各部分(要素、属性、テキストなど)を「ノード」として表現し、これらのノードが親子関係や兄弟関係を持つ階層的なツリーを構築します。
DOMによるXMLファイルの読み込み
DOMを使ってXMLファイルを読み込む基本的な手順は以下の通りです。
- `DocumentBuilderFactory`のインスタンスを生成します。
- `DocumentBuilderFactory`から`DocumentBuilder`のインスタンスを生成します。
- `DocumentBuilder`の`parse()`メソッドでXMLファイルを解析し、`Document`オブジェクトを取得します。
以下に具体的なサンプルコードを示します。
<?xml version="1.0" encoding="UTF-8"?>
<bookshelf> <book isbn="978-4-7741-9323-0"> <title>独習Java</title> <author>山田 太郎</author> </book> <book isbn="978-4-297-11822-5"> <title>スッキリわかるJava入門</title> <author>鈴木 次郎</author> </book>
</bookshelf>
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class DomParserExample { public static void main(String[] args) { try { File inputFile = new File("books.xml"); // 1. DocumentBuilderFactoryのインスタンスを取得 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); // 2. DocumentBuilderのインスタンスを取得 DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); // 3. XMLファイルをパースしてDocumentオブジェクトを取得 Document doc = dBuilder.parse(inputFile); // ドキュメントの正規化(テキストノードのマージなど) doc.getDocumentElement().normalize(); System.out.println("ルート要素: " + doc.getDocumentElement().getNodeName()); // "book"タグのノードリストを取得 NodeList nList = doc.getElementsByTagName("book"); for (int i = 0; i < nList.getLength(); i++) { Node nNode = nList.item(i); System.out.println("\n現在の要素: " + nNode.getNodeName()); if (nNode.getNodeType() == Node.ELEMENT_NODE) { Element eElement = (Element) nNode; // 属性の取得 System.out.println("ISBN: " + eElement.getAttribute("isbn")); // 子要素の取得 System.out.println("タイトル: " + eElement.getElementsByTagName("title").item(0).getTextContent()); System.out.println("著者: " + eElement.getElementsByTagName("author").item(0).getTextContent()); } } } catch (Exception e) { e.printStackTrace(); } }
}
第3章: SAXによるXML解析 – イベント駆動のストリーム処理
SAX (Simple API for XML) は、DOMとは対照的に、XMLドキュメントをメモリに保持せず、先頭から順に読み込んでいくストリームベースのAPIです。 ドキュメントを読み進める過程で、「要素の開始」「要素の終了」「テキストデータ」といった特定のイベントが発生すると、あらかじめ登録しておいたハンドラメソッドを呼び出します。 この仕組みはプッシュモデルと呼ばれ、パーサーがアプリケーションコードを呼び出す形で処理が進みます。
SAXによるXMLファイルの解析
SAXを使ってXMLファイルを解析する基本的な手順は以下の通りです。
- `DefaultHandler`を継承したイベントハンドラクラスを作成します。このクラスで`startElement`, `endElement`, `characters`などのメソッドをオーバーライドします。
- `SAXParserFactory`のインスタンスを生成します。
- `SAXParserFactory`から`SAXParser`のインスタンスを生成します。
- `SAXParser`の`parse()`メソッドに、XMLファイルと作成したハンドラのインスタンスを渡して解析を実行します。
以下に具体的なサンプルコードを示します。
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class SaxParserExample { public static void main(String[] args) { try { File inputFile = new File("books.xml"); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); BookHandler bookHandler = new BookHandler(); saxParser.parse(inputFile, bookHandler); } catch (Exception e) { e.printStackTrace(); } }
}
class BookHandler extends DefaultHandler { boolean bTitle = false; boolean bAuthor = false; // 要素の開始タグを読み込んだ時に呼ばれる @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equalsIgnoreCase("book")) { String isbn = attributes.getValue("isbn"); System.out.println("\nISBN: " + isbn); } else if (qName.equalsIgnoreCase("title")) { bTitle = true; } else if (qName.equalsIgnoreCase("author")) { bAuthor = true; } } // 要素の終了タグを読み込んだ時に呼ばれる @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equalsIgnoreCase("book")) { //System.out.println("End Element :" + qName); } } // テキストデータを読み込んだ時に呼ばれる @Override public void characters(char ch[], int start, int length) throws SAXException { if (bTitle) { System.out.println("タイトル: " + new String(ch, start, length)); bTitle = false; } else if (bAuthor) { System.out.println("著者: " + new String(ch, start, length)); bAuthor = false; } }
}
第4章: StAX – DOMとSAXの「いいとこ取り」プル型API
StAX (Streaming API for XML) は、DOMとSAXのそれぞれの利点を組み合わせることを目指したAPIです。 SAXと同じくストリームベースでメモリ効率が良いという特徴を持ちながら、SAXの「プッシュモデル」とは異なる「プルモデル」を採用しています。
プルモデルでは、アプリケーションが主体となってパーサーから次のイベントを要求(プル)します。 これにより、アプリケーションは自身のタイミングでXMLの読み込みを制御でき、SAXよりもプログラミングが直感的になります。
StAXには、主に2つのAPIスタイルがあります。
- カーソルAPI (`XMLStreamReader`): データベースのカーソルのように、XMLドキュメント内の特定の位置を指し示し、アプリケーションがそれを前後に動かしながら情報を読み取ります。 –
- イテレータAPI (`XMLEventReader`): `Iterator`パターンのように、イベントをオブジェクトとしてカプセル化し、`hasNext()`や`next()`でイベントを順次取得していきます。
StAX (カーソルAPI) によるXMLファイルの解析
ここでは、より一般的に使われるカーソルAPI (`XMLStreamReader`) を使った例を示します。
- `XMLInputFactory`のインスタンスを生成します。
- `XMLInputFactory`から`XMLStreamReader`のインスタンスを生成します。
- `hasNext()`で次のイベントが存在するか確認し、`next()`でイベントを進めながらループ処理を行います。
- `getEventType()`でイベントの種類を判別し、`isStartElement()`, `isEndElement()`, `getCharacters()`などで情報を取得します。
以下に具体的なサンプルコードを示します。
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.FileReader;
public class StaxParserExample { public static void main(String[] args) { boolean bTitle = false; boolean bAuthor = false; try { XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader streamReader = factory.createXMLStreamReader(new FileReader("books.xml")); while (streamReader.hasNext()) { int event = streamReader.next(); switch (event) { case XMLStreamConstants.START_ELEMENT: String qName = streamReader.getLocalName(); if ("book".equals(qName)) { System.out.println("\nISBN: " + streamReader.getAttributeValue(0)); } else if ("title".equals(qName)) { bTitle = true; } else if ("author".equals(qName)) { bAuthor = true; } break; case XMLStreamConstants.CHARACTERS: if (bTitle) { System.out.println("タイトル: " + streamReader.getText().trim()); bTitle = false; } if (bAuthor) { System.out.println("著者: " + streamReader.getText().trim()); bAuthor = false; } break; case XMLStreamConstants.END_ELEMENT: // 必要であれば終了タグの処理を記述 break; } } } catch (Exception e) { e.printStackTrace(); } }
}
第5章: JAXBによるデータバインディング – XMLとJavaオブジェクトの架け橋
JAXB (Java Architecture for XML Binding) は、XMLドキュメントとJavaオブジェクトを自動的に相互変換するための技術です。 これにより、開発者は低レベルな解析APIを直接触ることなく、使い慣れたJavaオブジェクトとしてXMLデータを操作できます。
- マーシャリング (Marshalling): JavaオブジェクトをXMLドキュメントに変換するプロセス。
- アンマーシャリング (Unmarshalling): XMLドキュメントをJavaオブジェクトに変換するプロセス。
JAXBでは、Javaクラスにアノテーションを付与することで、XMLの要素や属性とのマッピングを定義します。
JAXBによるXMLとオブジェクトの相互変換
JAXBを使うには、まずXMLの構造に対応するJavaクラスを作成し、アノテーションでマッピングを指定します。
対象XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book isbn="978-4-87311-904-9"> <title>Effective Java</title> <author>Joshua Bloch</author>
</book>
Javaクラス (POJO):
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
// XMLのルート要素を指定
@XmlRootElement
public class Book { private String isbn; private String title; private String author; // GetterとSetter (JAXBには必須) @XmlAttribute public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } @XmlElement public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @XmlElement public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @Override public String toString() { return "Book [isbn=" + isbn + ", title=" + title + ", author=" + author + "]"; }
}
アンマーシャリング (XML → Javaオブジェクト) の例:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
public class UnmarshalExample { public static void main(String[] args) { try { File file = new File("book_effective_java.xml"); JAXBContext jaxbContext = JAXBContext.newInstance(Book.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); Book book = (Book) jaxbUnmarshaller.unmarshal(file); System.out.println(book); } catch (JAXBException e) { e.printStackTrace(); } }
}
マーシャリング (Javaオブジェクト → XML) の例:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
public class MarshalExample { public static void main(String[] args) { Book book = new Book(); book.setIsbn("978-4-297-10886-8"); book.setTitle("プログラマを育てる脳のしくみ"); book.setAuthor("長瀬 嘉秀"); try { JAXBContext jaxbContext = JAXBContext.newInstance(Book.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); // 人が読みやすいようにフォーマットする設定 jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // コンソールに出力 jaxbMarshaller.marshal(book, System.out); // ファイルに出力 File file = new File("new_book.xml"); jaxbMarshaller.marshal(book, file); } catch (JAXBException e) { e.printStackTrace(); } }
}
Java 11以降のJAXBの利用
JAXBはJava 11で標準ライブラリから削除された最も影響の大きいAPIの一つです。 したがって、Java 11以降の環境でJAXBを利用するには、別途ライブラリをプロジェクトに追加する必要があります。詳細は次の章で解説します。
第6章: Java 9以降のモジュール化とjavax.xmlの未来
Javaの歴史における大きな転換点の一つが、2017年にリリースされたJava 9で導入されたJava Platform Module System (JPMS)、通称「Jigsawプロジェクト」です。これにより、JDK自身がモジュールに分割され、アプリケーションもモジュールとして定義できるようになりました。
この変更に伴い、`javax.xml`関連のAPI群は `java.se.ee` という集約モジュールにまとめられました。これは、Java SEプラットフォームに含まれているものの、本来はJava EE (現: Jakarta EE) の仕様であるAPI群をまとめたものです。
Java 11での標準ライブラリからの削除
そして、2018年にリリースされたJava 11では、この `java.se.ee` モジュールが標準ライブラリから完全に削除されました。 これには以下の主要なAPIが含まれます。
- JAXB (java.xml.bind)
- JAX-WS (java.xml.ws)
- Common Annotations (java.xml.ws.annotation)
- JavaBeans Activation Framework (java.activation)
- CORBA (java.corba)
この決定の背景には、Java SEプラットフォームを軽量化し、開発を迅速化するという目的がありました。
Java 11以降でjavax.xml.bind (JAXB) を使うには
Java 11以降のプロジェクトでJAXBを使おうとすると、`javax.xml.bind`パッケージが見つからず、コンパイルエラーや実行時エラー (`NoClassDefFoundError`) が発生します。 これを解決するには、ビルドツール(MavenやGradle)を使って、必要なライブラリを明示的に依存関係に追加する必要があります。
Mavenでの依存関係の追加例 (`pom.xml`)
JAXBのAPIとその参照実装を追加します。
<dependencies> <!-- JAXB API --> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>3.0.1</version> <!-- 2024年時点での安定版。最新はMaven Centralで確認 --> </dependency> <!-- JAXB Runtime --> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>3.0.2</version> <!-- APIバージョンと互換性のあるものを選択 --> <scope>runtime</scope> </dependency>
</dependencies>
`javax`から`jakarta`へ: Eclipse Foundationへの移管に伴い、多くのJava EE APIのパッケージ名が `javax.*` から `jakarta.*` へと変更されています。 新しいプロジェクトでは、`jakarta.xml.bind`のように`jakarta`名前空間のライブラリを使用することが推奨されます。
まとめ: どのAPIを選ぶべきか?
`javax.xml`パッケージが提供する豊富なAPI群を解説してきました。では、実際の開発ではどのAPIを選択すべきなのでしょうか。以下に選択の指針をまとめます。
API | 最適なシナリオ | 避けるべきシナリオ |
---|---|---|
DOM | XMLの構造を頻繁に変更(要素の追加・削除など)する必要がある場合。 ドキュメント内の様々な要素にランダムにアクセスしたい場合。 比較的小さなファイル(数MB程度まで)を扱う場合。 | 非常に大きなXMLファイル(数GBなど)を扱う場合(メモリ不足のリスク)。 単純な読み取り処理しか行わない場合(オーバースペック)。 |
SAX | 非常に大きなXMLファイルを、メモリをほとんど使わずに処理する必要がある場合。 特定の情報だけを抽出するような、単純な読み取り処理を行う場合。 | XMLの構造が複雑で、現在の解析位置を常に把握する必要がある場合(状態管理が煩雑になる)。 読み取った後にドキュメント構造を変更したい場合。 |
StAX | 大きなファイルをストリーム処理したいが、SAXよりもプログラムの制御を明確にしたい場合。 XMLの一部を読み取って、その情報に基づいて処理を分岐させるような場合。 | XMLドキュメント全体に対するランダムアクセスが必要な場合。 DOMほど直感的なツリー操作はできない。 |
JAXB | XMLの構造がスキーマなどで明確に定義されており、それをJavaオブジェクトとして扱いたい場合。 設定ファイルやWebサービスのレスポンス/リクエストなど、定型的なXMLを扱う場合。 | Java 11以降の環境で、依存ライブラリを追加できない、または追加したくない場合。 非常に自由な構造を持つ、非定型なXMLを扱う場合。 |
JavaとXMLを取り巻く環境は、Javaの進化とともに大きく変化しました。かつては標準であった`javax.xml`の多くの機能が、今では外部ライブラリとして管理する必要があります。この変化を正しく理解し、プロジェクトの要件や使用するJavaのバージョンに合わせて適切な技術を選定することが、現代のJava開発者には求められています。