JAXPによるDOM解析

revised: Oct./26th/03

JAXPとDOM

JAXP (Java APIs for XML Processing)は、実装に依存するDOMやSAXのAPIをラップすることで抽象化して、XMLパーサの実装に依存しない共通のAPIを提供する仕組みです。

実装に依存したAPIは、実装が特定できるのであれば、信頼性が高く、きめ細かな指定が可能となります。しかし、実装を変更するために、コードの変更/リコンパイルが必要となるため、相互運用性/保守性が減退することも事実です。JAXPに従えば、アプリケーションのコードを変更することなく、XMLパーサの実装を変更することが可能です。JAXPのパッケージは、J2SE 1.4ではコア・パッケージjavax.xml.parsersに含まれるようになり、JAXPのAPIのみで十分高度なアプリケーションが設計/開発できます。

JAXPでは、XMLパーサを直接newするのではなく、ファクトリー・メソッドとよばれる仕組みによって生成します。また、ファクトリ・メソッド用のクラスが抽象クラスで用意されているので、具象クラスを切り替えることで、異なったセットのインスタンスを生成することができるようになっています。これは、アブストラクト・ファクトリと呼ばれるデザインパターンに則ったものです。実装の詳細からコードが独立し、異なる実装へ切り替えた場合にも、生成されるオブジェクトを利用しているクライアント・クラスのコードを変更する必要がなくなります。

JAXPの概要

コードを見る前に、具体的な手順をまとめておきます。

まず、DOMツリーの全体を現すDocumentインスタンス用のファクトリ・クラスを生成するためのファクトリ・クラスを、DocumentBuilderFactory型で生成します。このオブジェクトもファクトリ・メソッドnewInstance()で生成します。このファクトリ・メソッドでは、次の順番でオブジェクトを生成する実装クラスを見つけ出します。

  1. javax.xml.parsers.DocumentBuilderFactoryシステム・プロパティ
  2. JREディレクトリ内のプロパティ・ファイル lib/jaxp.properties
  3. JARファイル内のMETA-INF/services/javax.xml.parsers.DocumentBuilderFactoryファイル
  4. プラットフォームのデフォルトのDocumentBuilderFactoryインスタンス

例えば、SDK 1.4には、Xerces以前にApacheで開発されていたCrimsonというXMLパーサが含まれています。これをシステム・プロパティjavax.xml.parsers.DocumentBuilderFactoryにて、実行時に明示的に指定するには、システム・プロパティ"-Dxxx=yyy"を使って、次のように指定します。

Crimsonを明示的に指定

>java -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.crimson.jaxp.DocumentBuilderFactoryImpl DomParseDemo3 demo2.xml

Xercesを明示的に指定

>java -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl DomParseDemo3 demo2.xml

生成されたDocumentBuilderFactory型オブジェクトに対して、さまざまな属性をセットできます。Xercesではパーサ固有のURIによってフィーチャーをセットしていましたが、JAXPでは属性固有のメソッドが用意されています。JAXPで用意されていない、XMLパーサの実装固有のフィーチャーをセットするためには、メソッドsetAttribute()が用意されています。JAXPで指定できる代表的な属性を表4に挙げました。

表4. DocumentBuilderFactoryで指摘できる属性
メソッドtrueの意味省略時値
setValidating(boolean value)妥当性検証を行うfalse
setNamespaceAware(boolean value)名前空間を認識するfalse
setIgnoringComments(boolean value)コメントを無視するfalse
setIgnoringElementContentWhitespace(boolean value)無視できる空白を無視するfalse
setAttribute(String name,Object value)指定した属性に値をセットするn/a

DOM Documentインスタンスは、ファクトリ・クラスDocumentBuilderによって生成し、DocumentBuilderDocumentBuilderFactoryによって生成します。生成したDocumentBuilderにはErrorHandlerをセットするのが普通です。

DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new MyHandler());
Document doc = builder.parse(arg[0]);

名前空間とXMLSchemaを認識するように属性をセットするには次のようにします。

// 名前空間を認識する
factory.setNamespaceAware(true);
// XML Scehmaを認識する
factory.setAttribute("http://apache.org/xml/features/validation/schema",new Boolean(true));

JAXPのサンプル

リスト10は、リスト8をJAXPで記述したものです。

リスト10. DomParseDemo4.java

import org.w3c.dom.*;
import org.apache.xerces.parsers.*;
import org.xml.sax.*;
import javax.xml.parsers.*;
class DomParseDemo4 {
    public static void main(String[] args) {
        try {
            // DOMパーサ用ファクトリの生成
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 妥当性検証を行う
            factory.setValidating(true);
            // 名前空間を認識する
            factory.setNamespaceAware(true);
            // XML Scehmaを認識する
            factory.setAttribute("http://apache.org/xml/features/validation/schema",true);
            // DOM Documentインスタンス用ファクトリの生成
            DocumentBuilder builder = factory.newDocumentBuilder();
            // エラー・ハンドラの登録
            builder.setErrorHandler(new MyHandler());
            
            // 解析とDocumentインスタンスの取得
            Document doc = builder.parse(args[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MyHandler implements ErrorHandler {
    public void warning(SAXParseException e) {
        System.out.println("警告: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
    public void error(SAXParseException e) {
        System.out.println("エラー: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
    public void fatalError(SAXParseException e) {
        System.out.println("深刻なエラー: " + e.getLineNumber() +"行目");
        System.out.println(e.getMessage());
    }
}

リスト12はリスト10の実行結果です。リスト4の名前空間を使ったXML文書をdemo5.xmlとして、同じディレクトリにXML Schema文書であるリスト6address.xsdとして保存しました。また、demo5.xmlの最初のitem要素で、sex属性の値をmanに変えたもの(リスト11)を検証すると、XML Schemaで未定義の属性値manが不正である旨が報告されます。

リスト11. demo6.xml

<?xml version="1.0" encoding="UTF-8"?>
<hlq:address xmlns:hlq="urn:address"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:address address.xsd">
    <hlq:item sex="man" custid="E21099">
        <hlq:name>菅井 学</hlq:name>
...

リスト17. JAXPによるDOM妥当性検証結果

>javac DomParseDemo4.java
>java DomParseDemo4 demo5.xml
>java DomParseDemo4 demo6.xml
エラー: 5行目
cvc-enumeration-valid: Value 'man' is not facet-valid with respect to enumeration '[male, female]'. It must be a value from the enumeration.
エラー: 5行目
cvc-attribute.3: The value 'man' of attribute 'sex' on element 'hlq:item' is not valid with respect to its type, 'null'.
>


Copyright © 2003 SUGAI, Manabu. All Rights Reserved.