JavaでXMLファイルを読み書きする方法

XMLファイルはデータ保存など、さまざまな目的に使用できます。JSONが普及する前は、XMLが構造化されたデータを表現、保存、転送するための好ましい形式でした。

XMLの人気が近年低下したとしても、ときどき見かけることがあるため、XMLの操作方法を学ぶことが重要です。DOM APIを使用してJavaでXMLファイルを読み書きする方法を確認しましょう。

JavaでXMLを処理するための要件

Java Standard Edition (SE)には、XML処理のほとんどの側面を網羅する包括的な用語であるJava API for XML Processing (JAXP)が含まれています。これらには以下が含まれます。

  • DOM:Document Object Modelには、要素、ノード、属性などのXMLオブジェクトを操作するためのクラスが含まれています。DOM APIは、処理のために完全なXMLドキュメントをメモリにロードします。そのため、大きなXMLファイルには適していません。
  • SAX:Simple API for XMLは、XMLを読み取るためのイベント駆動型APIです。ファイルの解析時に見つけたXMLコンテンツに応じてイベントを発生させます。このメソッドのメモリフットプリントは小さいですが、APIを操作するのはDOMを操作するよりも困難です。
  • StAX:Streaming API for XMLは最近追加されたものです。XMLの高性能なストリームフィルタリング、処理、変更を提供します。XMLドキュメント全体をメモリにロードするのを回避しますが、イベント駆動型アーキテクチャではなくプル型アーキテクチャを提供するため、SAX APIよりもコーディングが簡単です。

JavaでXMLを処理するには、次のパッケージをインポートする必要があります。

import javax.xml.parsers.*;import javax.xml.transform.*;import org.w3c.dom.*;

サンプルXMLファイルの準備

サンプルコードとその背後にある概念を理解するには、MicrosoftのこのサンプルXMLファイルを使用します。以下に抜粋を示します。

Gambardella, MatthewXML Developer's GuideComputer44.952000-10-01An in-depth look at creating applicationswith XML.Ralls, Kim...切り取り...

DOM APIを使用したXMLファイルの読み取り

DOM APIを使用してXMLファイルを読み取るために必要な基本的な手順を見てみましょう。まず、XMLドキュメントを解析するために使用するDocumentBuilderのインスタンスを作成します。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();

これで、XMLルート要素から始めて、ドキュメント全体をメモリにロードできます。この例では、catalog要素です。

// 読み取るXMLファイルFile file = "";Document document = builder.parse(file);Element catalog = document.getDocumentElement();

これで、ルート要素catalogから始まるXMLドキュメント全体にアクセスできるようになりました。

DOM APIを使用した情報の抽出

XMLルート要素が得られたので、DOM APIを使用して興味深い情報を抽出できます。たとえば、ルート要素のすべてのbook子要素を取得して、それらをループします。getChildNodes()は、テキスト、コメントなどを含むすべての子要素を返すことに注意してください。目的のためには、子要素だけが必要なので、他の要素はスキップできます。

NodeList books = catalog.getChildNodes();for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i++) {Node child = books.item(i);if ( child.getNodeType() != Node.ELEMENT_NODE )continue;Element book = (Element)child;// ここでbook要素を操作}

親が与えられた場合、特定の子要素を見つけるにはどうすればよいでしょうか。見つかった場合は一致する最初の要素を返し、見つからない場合はnullを返す静的メソッドを作成します。この手順には、子ノードのリストを取得し、指定された名前を持つ要素ノードを選択してループすることが含まれます。

static private Node findFirstNamedElement(Node parent,String tagName){NodeList children = parent.getChildNodes();for (int i = 0, in = children.getLength() ; i < in ; i++) {Node child = children.item(i);if (child.getNodeType() != Node.ELEMENT_NODE)continue;if (child.getNodeName().equals(tagName))return child;}return null;}

DOM APIは、要素内のテキストコンテンツを、TEXT_NODEタイプの別のノードとして扱います。テキストコンテンツは、複数の隣接するテキストノードで構成される場合があります。そのため、要素のテキストを取得するには、特別な処理が必要です。

static private String getCharacterData(Node parent){StringBuilder text = new StringBuilder();if ( parent == null )return text.toString();NodeList children = parent.getChildNodes();for (int k = 0, kn = children.getLength() ; k < kn ; k++) {Node child = children.item(k);if (child.getNodeType() != Node.TEXT_NODE)break;text.append(child.getNodeValue());}return text.toString();}

これらの便利な関数を備えて、このコードを見て、サンプルXMLから情報をリストアップしましょう。カタログに入手可能な各書籍の詳細情報を表示します。

NodeList books = catalog.getChildNodes();for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i++) {Node child = books.item(i);if (child.getNodeType() != Node.ELEMENT_NODE)continue;Element book = (Element)child;ii++;String id = book.getAttribute("id");String author = getCharacterData(findFirstNamedElement(child, "author"));String title = getCharacterData(findFirstNamedElement(child, "title"));String genre = getCharacterData(findFirstNamedElement(child, "genre"));String price = getCharacterData(findFirstNamedElement(child, "price"));String pubdate = getCharacterData(findFirstNamedElement(child, "pubdate"));String descr = getCharacterData(findFirstNamedElement(child, "description"));System.out.printf("%3d. book id = %s\n" +" author: %s\n" +" title: %s\n" +" genre: %s\n" +" price: %s\n" +" pubdate: %s\n" +" descr: %s\n",ii, id, author, title, genre, price, pubdate, descr);}

コードの説明をステップバイステップで行います。

  1. コードは、ルート要素であるcatalogの子ノードを反復処理します。
  2. 書籍を表す各子ノードについて、ノードのタイプがELEMENT_NODEであるかどうかを確認します。そうでない場合は、次の反復処理に進みます。
  3. 子ノードがELEMENT_NODEの場合、(Element)childによってElementオブジェクトにキャストされます。
  4. 次に、コードは "id"、"author"、"title"、"genre"、"price"、"pub date"、"description" を含む書籍要素からさまざまな属性と文字データを取り出します。System.out.printfメソッドを使用して、このデータを印刷します。

出力は次のようになります。

Transform APIを使用したXML出力の書き込み

Javaは、XMLデータを変換するためのXML Transform APIを提供しています。このAPIをアイデンティティ変換で使用して、出力を生成します。たとえば、上で示したサンプルカタログに新しいbook要素を追加してみましょう。

プロパティファイルやデータベースなどの外部ソースから書籍の詳細(著者、タイトルなど)を取得することがあります。例として、次のプロパティファイルを使用できます。

id=bk113author=Jane Austentitle=Pride and Prejudicegenre=Romanceprice=6.99publish_date=2010-04-01description="It is a truth universally acknowledged, that a single man in possession of a good fortune must be in want of a wife." So begins Pride and Prejudice, Jane Austen's witty comedy of manners-one of the most popular novels of all time-that features splendidly civilized sparring between the proud Mr. Darcy and the prejudiced Elizabeth Bennet as they play out their spirited courtship in a series of eighteenth-century drawing-room intrigues.

最初のステップは、上で示したメソッドを使用して既存のXMLファイルを解析することです。

File file = ...; // XML file to readDocument document = builder.parse(file);Element catalog = document.getDocumentElement();

次に、Javaで提供されているPropertiesクラスを使用して、プロパティファイルをロードします。コードは非常にシンプルです。

String propsFile = "";Properties props = new Properties();try (FileReader in = new FileReader(propsFile)) {props.load(in);}

プロパティをロードしたら、プロパティファイルから追加する値を取得できます。

String id = props.getProperty("id");String author = props.getProperty("author");String title = props.getProperty("title");String genre = props.getProperty("genre");String price = props.getProperty("price");String publish_date = props.getProperty("publish_date");String descr = props.getProperty("description");

次に、空のbook要素を作成します。

Element book = document.createElement("book");book.setAttribute("id", id);

子要素をbookに追加するのは簡単です。便宜上、必要な要素名をListに収集し、ループで値を追加できます。

List elnames =Arrays.asList("author", "title", "genre", "price","publish_date", "description");for (String elname : elnames) {Element el = document.createElement(elname);Text text = document.createTextNode(props.getProperty(elname));el.appendChild(text);book.appendChild(el);}catalog.appendChild(book);

catalog要素には、新しいbook要素が追加されました。残っているのは、更新されたXMLを書き出すことだけです。

XMLを書き出すには、Transformerのインスタンスが必要です。次のように作成できます。

TransformerFactory tfact = TransformerFactory.newInstance();Transformer tform = tfact.newTransformer();tform.setOutputProperty(OutputKeys.INDENT, "yes");tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");

setOutputProperty()を使用して、出力のインデントを要求できます。

最後のステップは、変換を適用することです。結果は出力ストリームSystem.outに表示されます。

tform.transform(new DOMSource(document), new StreamResult(System.out));

出力を直接ファイルに書き込むには、次のを使用します。

tform.transform(new DOMSource(document), new StreamResult(new File("output.xml")));

これが、JavaでXMLファイルを読み書きするために必要なすべての手順です。

これで、JavaでXMLファイルを読み書きする方法がわかりました

JavaでXMLを解析して操作することは、実際のプログラムでよく使用する貴重なスキルです。DOM APIとTransform APIは特に便利です。

特にDOMを理解することは、Webアプリケーションやサイトのクライアント側コードを記述する予定がある場合に不可欠です。DOMのインターフェースは普遍的であるため、JavaやJavaScriptなどさまざまな言語で同様のコードを使用して操作できます。