Java Tips:JAXPを使ってXML文書を読み込むにはJAVA Developer

JavaのプログラムからXML文書(ファイル)にアクセスする手段は、XML文書を処理するXMLプロセッサに依存します。J2SEでは標準APIとしてJAXPを提供しており、特定のXMLプロセッサに依存することなくXML文書を読み込むことができます。

» 2004年07月20日 11時07分 公開
[JAVA Developer]

XML文書の読み込み方法にはDOMとSAXがある

 WebアプリケーションにおいてXML文書を利用する機会が増えており,XML文書を利用するためのソリューションも整いつつあります。ここでは,XML文書を利用するための基礎として,DOM(Document Object Model)によるXML文書の読み込み方法を紹介します。なお,XML文書の基本的な読み込み方法としては,DOMのほかにSAX(Simple API for XML)を利用する方法もあります。次の表は,DOMとSAXの一般的な特徴を整理したものです。

表1 DOMとSAXの違い

DOMSAX
APIの特徴ツリー構造に基づくAPIイベント駆動型API
XML文書へのアクセスXML文書の解析後にアクセス可能基本的にXML文書の解析中のみ
ツリー構造保持する保持しない
メモリー使用量XML文書に依存XML文書の大きさに関わらず少ない
大きなXML文書あまり向かない向いている

 JAXPとDOMによってXML文書を読み込む場合は,リスト1に示すようにjavax.xml.parsers.DocumentBuilderFactoryオブジェクトを作り,さらにjavax.xml.parsers.DocumentBuilderオブジェクトを作ります。

リスト1 DOMを使ったXML文書の読み込み(抜粋)

String url = "test.xml";
 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(url); // XML文書をパース
 
Element element = doc.getDocumentElement(); // ルート要素の取得
NodeList list = element.getChildNodes();    // 子要素を含んだノードを列挙
Node node;
for (int iNode=0; (node = list.item(iNode))!=null; iNode++) {
    // ノードの処理
}

 太字のDocumentBuilder#parseメソッドが,実際にXML文書を読み込みパース(解析)する部分です。パースした結果をorg.w3c.dom.Documentオブジェクトに格納しています。このDocumentオブジェクトは,XML(またはHTML)文書全体を表すもので,これを使って文書ツリーにアクセスします。具体的には,Document#getDocumentElementメソッドを使ってルート要素(org.w3c.dom.Elementオブジェクト)を取得。次にElement#getChildNodesメソッドでその要素を構成する子ノード一覧を表すorg.w3c.dom.NodeListオブジェクトを取得し,各ノードにアクセスします。

 ノードから取得できる情報は,ノード型(nodeType)によって異なります。

表2 ノードが保持する情報(値)の違い

名前属性
ATTRIBUTE_NODE属性名属性値null
CDATA_SECTION_NODE"#cdata-section"CDATAセクションのコンテンツnull
COMMENT_NODE"#comment"コメントのコンテンツnull
DOCUMENT_NODE"#document"nullnull
DOCUMENT_FRAGMENT_NODE"#document-fragment"nullnull
DOCUMENT_TYPE_NODE文書型名nullnull
ELEMENT_NODEタグ名nullNamedNodeMap
ENTITY_NODEエンティティ名nullnull
ENTITY_REFERENCE_NODE参照されるエンティティの名前nullnull
NOTATION_NODE表記法名nullnull
PROCESSING_INSTRUCTION_NODEターゲットターゲットを除くすべてのコンテンツnull
TEXT_NODE"#text"テキストノードのコンテンツnull

 次にサンプルのXML文書を示します。

リスト2 テスト用のXML文書

<?xml version="1.0" encoding="utf-8" ?>
<data>
  <item type="private">
    <title>asahi.com</title>
    <link>http://www.asahi.com/</link>
    <description />
  </item>
</data>

 DOMでは,XML文書を細分化し,それぞれをノードとして扱います。たとえばルート要素であるdataやその子要素であるitemは,ノード型ELEMENT_NODEのノードとなり,item要素の属性であるtypeはノード型ATTRIBUTE_NODEのノードとなります。また,要素の内容を示すasahi.comやhttp://www.asahi.com/はTEXT_NODEのノードとなります。このほか,タグとタグの間にある改行やスペースもTEXT_NODEのノードとして扱われます。

 具体的にプログラムを見てみましょう。リスト3は,item要素の属性とその子要素の内容を取得するものです。

リスト3 DOMを使った要素の属性と内容の取得(抜粋)

Element element = doc.getDocumentElement(); // ルート要素の取得
NodeList list = element.getChildNodes();    // 子要素を含んだノードを列挙
Node node;
for (int iNode=0; (node = list.item(iNode))!=null; iNode++) {
    String nodeName = node.getNodeName();
    if (nodeName.equals("item")) {
        System.out.println(nodeName);
        // (1)要素の属性を取得
        NamedNodeMap attrMap = node.getAttributes();
        int attrs = attrMap.getLength();
        for (int iAttr=0; iAttr<attrs; iAttr++) {
            Node attr = attrMap.item(iAttr);
            System.out.println(
                    attr.getNodeName() + "=" + attr.getNodeValue());
        }
        // (2)子要素を取得
        NodeList childNodes = node.getChildNodes();
        Node childNode;
        for (int iItem=0;
                (childNode = childNodes.item(iItem))!=null; iItem++) {
            String childName = childNode.getNodeName();
            if (childNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                String value = null;
                try {
                    value = childNode.getFirstChild().getNodeValue();
                }
                catch (NullPointerException e) {
                    // 内容なし
                }
                System.out.println(
                        childNode.getNodeName() + ">"+ value);
            }
        }
    }
}

 要素の属性は,getAttributesメソッドを使ってorg.w3c.dom.NamedNodeMapオブジェクトを取得します。ここではすべての属性(ノード)を順番に取得していますが,NamedNodeMap#getNamedItemメソッドに属性名を与えてその属性を取得することも可能です。

 子要素の取得は,ルート要素の場合と同じNode#getChildNodesメソッドを使います。ここでは,ノード型を取得して要素(ELEMENT_NODE)であれば,その子ノード(TEXT_NODE)の値を取得して表示しています。リスト2のように内容が省略されている場合,要素のを格納する子ノードは存在せず,getFirstChildメソッドの戻り値はnullになります。リスト3では,その例外(NullPointerException)をキャッチして対応しています。

 リスト3を実行すると,コンソールには次のように表示されます。リスト2がパースされ,要素の属性や内容を取得できていることがわかります。

item
type=private
title>asahi.com
link>http://www.asahi.com/
description>null

 「JAVA Developer」より毎週役立つJava Tipsを配信中。ほかにも参考になるTipsは、JAVA Developerサイトのバックナンバーから探すことが可能です。

Copyright(C) 2010 SOFTBANK Creative Inc. All Right Reserved.

注目のテーマ