ヘッダーをスキップ
Oracle XML Developer's Kitプログラマーズ・ガイド
11gリリース1(11.1)
E05676-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

4 XML Parsing for Java

この章の内容は次のとおりです。

XML Parsing for Javaの概要

この項の内容は次のとおりです。

前提条件

Oracle XML Parsingは、XML文書を読み取り、DOMまたはSAX APIを使用して、そのコンテンツと構造へのプログラム・アクセスを可能にします。この解析は検証モードまたは非検証モードで使用できます。

この章では、次のテクノロジを十分に理解していると想定します。

前述のテクノロジの概要説明が必要な場合は、「はじめに」の「関連ドキュメント」にあるXMLの資料を参照してください。

標準と仕様

DOMレベル1、レベル2およびレベル3の仕様はW3C勧告です。次のURLに、3つのレベルすべての仕様へのリンクがあります。

http://www.w3.org/DOM/DOMTR

SAXにはバージョン1.0と2.0があり、バージョン1.0は非推奨です。これはW3C仕様ではありません。次のURLに、SAXのドキュメントがあります。

http://www.saxproject.org/

XML名前空間はW3C勧告です。仕様は次のURLにあります。

http://www.w3.org/TR/REC-xml-names

JCR 1.0(JSR 170とも呼ばれます)は、アプリケーションがコンテンツ・リポジトリと対話するための標準Java APIを定義しています。


関連項目:

『Oracle XML DB開発者ガイド』

JAXPバージョン1.2には、XSLTフレームワークに加えて、DOMレベル2およびSAXバージョン2.0をサポートするための解析APIに対する更新と、プラッガブルな実装を特定するための改良されたスキームが含まれています。JAXPでは、XMLスキーマとXSLTコンパイラがサポートされています。次のURLに、Sun社が作成したJAXP仕様があります。

http://java.sun.com/xml/downloads/jaxp.html

関連項目:

XDKがサポートする標準の説明は、第31章「XDK標準」を参照してください。

巨大ノードの処理

XMLノードへのDOMストリーム・アクセスは、PL/SQLおよびJavaのAPIによって実行されます。XML文書内のノードは64Kバイトを大幅に超えます。このため、JPEG、Word、PDF、RTFおよびHTML文書を容易に格納できます。


関連項目:

Javaの巨大ノードに関する機能の詳細は、『Oracle XML DB Developer's Guide』を参照してください。

JavaでのXML解析

XMLParserは、XML Parser for Javaの抽象ベース・クラスです。インスタンス化されたパーサーは、parse()メソッドを起動してXML文書を読み取ります。

XMLDOMImplementationファクトリ・メソッドは、別のメソッドを提供してバイナリXMLを解析し、拡張性のあるDOMを作成します。

図4-1に、XMLParserを使用した解析の基本プロセスを示します。この図はXMLDOMImplementation()には適用されません。

図4-1 XML Parserのプロセス

この図については前後のテキストで説明しています。
「図4-1 XML Parserのプロセス」の説明

Javaアプリケーションでは、次のAPIを使用して、解析済XML文書にアクセスできます。

  • DOM APIは、XML文書を解析し、メモリー内に文書のツリー表現を構築します。DOMParserオブジェクトを使用してDOM解析をするか、XMLDOMImplementationインタフェース・ファクトリ・メソッドを使用してプラグ可能な拡張性のあるDOMを作成するかのいずれかをします。

  • SAX APIは、XML文書をイベントのストリームとして処理します。したがって、プログラムから文書内のランダムな位置へのアクセスはできません。SAXを使用した解析にはSAXParserオブジェクトを使用します。

  • JAXPは、DOM、SAXおよびXSLをサポートするJava固有のAPIです。JAXPを使用した解析にはDocumentBuilderまたはSAXParserオブジェクトを使用します。

例4-1に、DOM、SAX、JAXPの違いを明示するサンプルXML文書を示します。

例4-1 サンプルXML文書

<?xml version="1.0"?>
  <EMPLIST>
    <EMP>
     <ENAME>MARY</ENAME>
    </EMP>
    <EMP>
     <ENAME>SCOTT</ENAME>
    </EMP>
  </EMPLIST>

XML解析でのDOM

DOMは、メモリー内にXML文書のツリー表現を構築します。たとえば、DOM APIでは例4-1に示した文書を受け取り、図4-2に示すメモリー内ツリーを作成します。DOMは、ツリーをナビゲートおよび処理するためのクラスおよびメソッドを提供します。

一般的に、DOM APIには次の利点があります。

  • オブジェクトのツリー構造が提供されるため、DOM APIはSAXよりも簡単に使用できます。

  • 要素の並替え、要素や属性の追加と削除、要素名の変更など、XMLツリーの構造的な操作を実行できます。

  • 対話型アプリケーションでメモリー内にオブジェクト・モデルを格納できるため、ユーザーがオブジェクト・モデルにアクセスして操作できます。

  • 標準としてのDOMはXPathをサポートしません。ただし、ほとんどのXPath実装ではDOMが使用されています。Oracle XDKには、XPathをサポートするためのDOM API拡張が含まれています。

  • プラグ可能な拡張性のあるDOMは、スケーラビリティと能率性を大幅に改善するように作成されます。

DOM作成

Java XDKでは、DOMを作成するのに3つの方法があります。

  • DOMParserを使用して文書を解析します。これは従来どおりのXDKの方法です。

  • XMLDOMImplementationファクトリ・メソッドを使用して拡張性のあるDOMを作成します。

  • XMLDocumentコンストラクタを使用します。これはXDKの一般的なソリューションではありません。

拡張性のあるDOM

Oracle 11gリリース1(11.1)では、XDKはプラグ可能な拡張性のあるDOMをサポートしています。これにより、メモリーの非効率、限定的なスケーラビリティ、DOM構成の制御不足という問題が緩和されます。

拡張性のあるDOMに関しては、構成および作成は主にXMLDOMImplementationクラスを使用してサポートされます。

これらは拡張性のあるDOMの重要な側面です。

  • プラグイン・データにより、外部XML表現は内部表現の複製XMLを使用せずに、直接、拡張性のあるDOMによって使用が可能になります。

    拡張性のあるDOMは、ReaderおよびInfosetWriter抽象インタフェースを介してプラグインXMLデータの上位で作成されます。XMLデータは、バイナリXML、XMLTypeおよびサード・パーティ製のDOMなどとは形式が異なる可能性もあります。

  • 一時ノード。DOMノードは容易に作成でき、使用中でない場合は解放できます。

  • バイナリXML

    拡張性のあるDOMは、入力および出力形式ともにバイナリXMLを使用できます。拡張性のあるDOMは、次の2つの方法でデータと対話します。

    • 抽象InfosetReaderおよびInfosetWriterインタフェースを介して対話。ユーザーは、(1)InfosetReaderおよびInfosetWriterBinXML実装を使用して、BinXMLデータの読取りおよび書込みを実行でき、(2)ユーザーが提供する他の実装を使用して、XML Information Setの他の形式に読取りおよび書込みを実行できます。

    • BinXMLStreamに対するInfosetReaderおよびInfosetWriterアダプタの実装を介して対話。

拡張性のあるDOMのサポートは次のもので構成されています。

プラグ可能なDOMサポート

プラグ可能なDOMは、データ・レイヤーからDOM APIを分割可能にするXDKメカニズムです。DOM APIは、InfosetReaderおよびInfosetWriterインタフェースによってデータから分離されます。

プラグ可能なDOMを使用すると、1つのプロセッサから別のプロセッサへのXMLデータの移動が容易になります。

DOM APIには、データの上位に統合された標準APIが含まれており、ノード・アクセス、ナビゲーション、更新プロセスおよび検索機能がサポートされています。

遅延マテリアライズ

遅延マテリアライズ・メカニズムを使用すると、XDKはアクセスするノードのみを作成し、使用しないノードをメモリーから解放します。スケーラビリティが向上するため、非常に大きなXML文書も処理可能です。

構成可能なDOM設定

DOM構成は他のアプリケーションに適合するように構成されています。DOMの構成には、読取り専用、ストリーム、一時更新およびシャドウ・コピーなど様々なアクセス・パターンを使用して、最大限のメモリーを使用および実行可能にできます。

XML ParserでのSAX

DOMとは異なりSAXはイベントベースであるため、入力文書のメモリー内ツリー表現を構築しません。SAXは、要素単位で入力文書を処理し、イベントと重要なデータをアプリケーションのコールバック・メソッドに報告できます。例4-1に示すXML文書は、図4-2に示す一連の線形イベントとして解析されます。

一般的に、SAX APIには次の利点があります。

  • XMLツリーの操作が必要でない検索操作やその他のプログラムに有用です。

  • メモリー・リソースを大量に消費しません。

  • データベースからXML文書を取得する場合はDOMよりも高速です。

図4-2 DOM(ツリーベース)APIとSAX(イベントベース)APIの比較

この図については前後のテキストで説明しています。
「図4-2 DOM(ツリーベース)APIとSAX(イベントベース)APIの比較」の説明

XML ParserでのJAXP

JAXP APIを使用すると、SAXまたはDOMパーサーの実装をプラグインできます。Oracle XDKで提供されているSAX APIとDOM APIは、JAXPがサポートするベンダー固有実装の例です。

一般的にJAXPの利点は、相互運用可能なアプリケーションを作成できることです。アプリケーションでは、JAXPを介して使用可能な機能を使用する場合、非常に簡単に実装を切り替えることができます。

JAXPの主な短所は、ベンダー固有のAPIよりも実行速度が遅いことです。また、JAXP APIでは使用できず、Oracle固有のAPIを介して使用できる機能もあります。Oracle固有の機能は、その一部のみが、JAXPに用意されている拡張メカニズムを介して使用できます。ただし、アプリケーションでこれらの拡張を使用する場合は、実装を柔軟に切り替えることはできません。

XML Parserでの名前空間のサポート

XML Parser for Javaは、非修飾の要素タイプおよび属性名と、名前空間内の要素タイプおよび属性名を解析できます。名前空間は、汎用の名前を提供することにより、XML文書内にある要素タイプ間または属性間の名前の競合を解決または回避するメカニズムです。例4-2に示すXML文書について考えてみます。

例4-2 名前空間を使用しないサンプルXML文書

<?xml version='1.0'?>
<addresslist>
  <company>
    <address>500 Oracle Parkway,
             Redwood Shores, CA 94065
    </address>
  </company>
  <!-- ... -->
  <employee>
    <lastname>King</lastname>
    <address>3290 W Big Beaver
             Troy, MI 48084
    </address>
  </employee>
  <!-- ... -->
</addresslist>

名前空間を使用しない場合、例4-2のXML文書を処理するアプリケーションでは、<address>タグが会社の住所と従業員の住所のどちらを参照しているかを判断できません。例4-3に示すように、名前空間を使用して<address>タグを区別できます。例では、次のXML名前空間を宣言します。

http://www.oracle.com/employee
http://www.oracle.com/company

例4-3では、com接頭辞を最初の名前空間に関連付け、emp接頭辞を2番目の名前空間に関連付けています。したがって、アプリケーションは<com:address><emp:address>を区別できます。

例4-3 名前空間を使用したサンプルXML文書

<?xml version='1.0'?>
<addresslist>
<!-- ... -->
  <com:company
    xmlns:com="http://www.oracle.com/company">
    <com:address>500 Oracle Parkway,
             Redwood Shores, CA 94065
    </com:address>
  </com:company>
  <!-- ... -->
  <emp:employee
    xmlns:emp="http://www.oracle.com/employee">
    <emp:lastname>King</emp:lastname>
    <emp:address>3290 W Big Beaver
             Troy, MI 48084
    </emp:address>
</emp:employee>

名前空間を使用する文書を解析する際に、次の用語を覚えておくと役に立ちます。

  • 名前空間の接頭辞は、xmlnsで宣言された名前空間接頭辞です。例4-3では、empおよびcomが名前空間の接頭辞です。

  • ローカル名は、名前空間の接頭辞が付いていない要素または属性の名前です。例4-3では、employeeおよびcompanyがローカル名です。

  • 修飾名は、ローカル名に接頭辞を加えたものです。例4-3では、emp:employeeおよびcom:companyが修飾名です。

  • 名前空間URIは、xmlnsに割り当てられているURIです。例4-3では、http://www.oracle.com/employeeおよびhttp://www.oracle.com/companyが名前空間URIです。

  • 拡張名は、名前空間の接頭辞を名前空間URIで置換することで取得されます。例4-3では、http://www.oracle.com/employee:employeeおよびhttp://www.oracle.com/company:companyが拡張要素名です。

XML Parserでの検証

アプリケーションは、parse()メソッドを起動してXML文書を解析します。通常、アプリケーションはparse()メソッドに関連して初期化メソッドおよび終了メソッドを起動します。oracle.xml.parser.v2.XMLParserに定義されているsetValidationMode()メソッドを使用して、パーサーのモードを検証または非検証に設定できます。

検証モードのXMLパーサー(検証XMLパーサー)は、DTDまたはXMLスキーマで指定されたルールに従ってXML文書を解析し、文書が指定したDTDまたはXMLスキーマに準拠しているかどうかを判断します。XML文書が準拠しない場合は文書が有効であり、文書の構造はDTDまたはスキーマのルールに準拠しています。非検証モードのパーサー(非検証パーサー)では、整形式であるかどうかのみが確認されます。

表4-1に、Oracle XDK Parserの検証モードを設定するためにsetValidationMode()で使用できるフラグを示します。

表4-1 XML Parser for Javaの検証モード

名前 XML Parserによる処理

非検証モード

NONVALIDATING

XMLが整形式であることを確認し、データを解析します。

DTD検証モード

DTD_VALIDATION

XMLが整形式であることを確認し、XMLデータがDTDに対して妥当であるかどうかを検証します。<!DOCTYPE>宣言で定義されたDTDは、入力XML文書の位置に相対的である必要があります。

スキーマ検証モード

SCHEMA_VALIDATION

XML文書を、その文書に対して指定されたXMLスキーマに従って検証します。

LAX検証モード

SCHEMA_LAX_VALIDATION

スキーマ定義が検出されなくなるまで、インスタンス・ドキュメントの一部またはすべての検証を試行します。定義が検出されない場合も、エラーは発生しません。schemaディレクトリのサンプル・プログラムXSDLax.javaを参照してください。

厳密な検証モード

SCHEMA_STRICT_VALIDATION

インスタンス・ドキュメント全体の検証を試行します。スキーマ定義が検出されない場合またはインスタンスが定義に準拠しない場合は、エラーが発生します。

部分検証モード

PARTIAL_VALIDATION

DTD(存在する場合)に従って、入力XML文書のすべてまたは一部を検証します。DTDが存在しない場合、パーサーは非検証モードに設定されます。

自動検証モード

AUTO_VALIDATION

DTDまたはXMLスキーマ(存在する場合)に従って、入力XML文書のすべてまたは一部を検証します。DTDとXMLスキーマのいずれも存在しない場合、パーサーは非検証モードに設定されます。


setValidationMode()での検証モードの設定に加えて、oracle.xml.parser.schema.XSDBuilderクラスを使用してXMLスキーマを構築し、XMLParser.setXMLSchema()メソッドを起動することにより、このXMLスキーマを使用するようにパーサーを構成できます。この場合、XMLパーサーは検証モードを自動的にSCHEMA_STRICT_VALIDATIONに設定し、schemaLocationおよびnoNamespaceSchemaLocation属性を無視します。検証モードをSCHEMA_LAX_VALIDATIONに変更することもできます。XMLParser.setDoctype()メソッドは、DTDのパラレル・メソッドですが、setXMLSchema()とは異なり、検証モードを変更しません。


関連項目:

  • 検証の詳細は、第7章「Schema Processor for Javaの使用」を参照してください。

  • XMLParserクラスおよびXSDBuilderクラスの詳細は、『Oracle Database XML Java API Reference』を参照してください。


XML Parserでの圧縮

XML Parserに実装されているXML Compressorを使用して、XML文書を圧縮および解凍できます。圧縮アルゴリズムは、XMLタグのトークン化に基づいています。これは、すべてのXML文書にはタグの繰返しがあるため、それらのタグをトークン化すると、データが大幅に圧縮されることを前提としています。圧縮の程度は、文書のタイプに依存します。タグの数が多く、テキスト・コンテンツが少ないほど、圧縮率が向上します。

Oracle XML Parserは、メモリー内のDOMツリー、またはXML文書から生成されたSAXイベントから、圧縮したバイナリ出力を生成します。表4-2に、2種類の圧縮を示します。

表4-2 DOMとSAXでのXML圧縮

タイプ 説明 圧縮API

DOMベース

目的は、DOMツリーの構造情報および階層情報を失うことなく、XML文書のサイズを小さくすることです。パーサーでは、解析済XML文書に対応するメモリー内DOMツリーがシリアライズされ、圧縮したXML出力ストリームが生成されます。シリアライズされたストリームは、再度読み取られると、DOMツリーを再生成します。

writeExternal()メソッドを使用して、圧縮したXMLを生成し、readExternal()メソッドを使用して、そのXMLを再構築します。これらのメソッドはoracle.xml.parser.v2.XMLDocumentクラス内にあります。

SAXベース

SAXパーサーでは、XMLファイルを解析する際に圧縮したストリームが生成されます。SAXパーサーによって生成されたSAXイベントは、SAX圧縮ユーティリティによって処理されます。SAX圧縮ユーティリティは、圧縮したバイナリ・ストリームを生成します。バイナリ・ストリームが再度読み取られると、SAXイベントが生成されます。

圧縮したXMLを生成するには、出力ストリームをコンストラクタに渡すことにより、oracle.xml.comp.CXMLHandlerBaseをインスタンス化します。オブジェクトをSAXParser.setContentHandler()に渡してから、parse()メソッドを実行します。XMLを解凍するにはoracle.xml.comp.CXMLParserクラスを使用します。

注意: CXMLHandlerBaseは、SAX 1.0と2.0の両方を実装していますが、1.0は非推奨です。2.0 APIを使用することをお薦めします。


DOMおよびSAXから生成された圧縮ストリームには互換性があるため、SAXから生成された圧縮ストリームを使用してDOMツリーを生成したり、DOMから生成された圧縮ストリームを使用してSAXイベントを生成したりできます。通常、XML文書と同様に、圧縮したXMLデータ出力はデータベースにBLOBとして格納できます。

プログラムで大きなXML文書を解析し、メモリー内にDOMツリーを作成すると、パフォーマンスに影響する場合があります。DOMツリーをシリアライズすることにより、XML文書をバイナリ・ストリームに圧縮できます。圧縮したストリーム内のXMLデータを検証せずに、DOMツリーを再生成できます。圧縮したストリームはシリアライズされたストリームとして処理できますが、ストリーム内のデータは、Javaのデフォルトのシリアライズによって実装される圧縮よりも厳密に制御および管理されます。


注意:

Oracle Textは、圧縮されたXML文書を検索できません。解凍によりパフォーマンスが低下します。クライアントとサーバー間でファイルを転送している場合は、HTTP圧縮の方が簡単です。

XML Parsing for Javaの使用: 概要

XML開発の基本コンポーネントはXML Parsingです。XML Parsing for JavaはスタンドアロンのXMLコンポーネントであり、プログラムで処理できるように、XML文書(あるいはスタンドアロンのDTDまたはXMLスキーマ)を解析します。この項の内容は次のとおりです。


注意:

パーサーは、サポートされている任意のJavaVMとともに使用できます。Oracle9i以上では、パーサーをデータベースにロードし、内部Oracle9i JVMを使用できます。その他のデータベース・バージョンでは、外部JVM内でパーサーを実行し、JDBCを介してデータベースに接続します。

XML Parser for Javaの使用: 基本プロセス

図4-3に、標準的なXML処理アプリケーションでXMLパーサーを使用する方法を示します。

図4-3 XML Parser for Java

この図については前後のテキストで説明しています。
「図4-3 XML Parser for Java」の説明

図4-3に示したアプリケーションの基本プロセスは次のとおりです。

  1. DOMまたはSAXパーサーにより、入力XML文書が解析されます。たとえば、プログラムはXMLデータ文書、DTD、XMLスキーマおよびXSLスタイルシートを解析できます。

  2. 検証パーサーを実装すると、プロセッサは、提供されているDTDまたはXMLスキーマに対してXMLデータ文書が妥当であるかどうかの検証を試行します。


関連項目:


XML Parserのデモ・プログラムの実行

XML Parser for Javaのデモ・プログラムは、$ORACLE_HOME/xdk/demo/java/parserにあります。表4-3に、デモ・プログラムが格納されているサブディレクトリを示します。

表4-3 Javaパーサーのデモ

ディレクトリ 内容 プログラムの説明

common

class.xml
DemoUtil.java
empl.xml
family.dtd
family.xml
iden.xsl
NSExample.xml
traversal.xml

XMLパーサーで一般的に使用するXMLファイルおよびJavaプログラムです。たとえば、XSLTスタイルシートiden.xslを使用して、XMLファイルの識別情報変換を実行できます。DemoUtil.javaでは、ファイル名からURLを作成するためのヘルパー・メソッドが実装されます。このメソッドは、他の多数のデモ・プログラムで使用されます。

comp

DOMCompression.java
DOMDeCompression.java
SAXCompression.java
SAXDeCompression.java
SampleSAXHandler.java
sample.xml
xml.ser

DOMおよびSAX圧縮を示します。

  • DOMCompression.javaでは、DOMツリーが圧縮されます。

  • DOMDeCompression.javaでは、圧縮したストリームからDOMが再度読み取られます。

  • SAXCompression.javaでは、SAXパーサーからの出力が圧縮されます。

  • SAXDeCompression.javaでは、圧縮したストリームからSAXイベントが再生成されます。

  • SampleSAXHandler.javaでは、SAX DeCompressorからスローされたイベントを処理するハンドラの使用方法が示されます。

dom

AutoDetectEncoding.java
DOM2Namespace.java
DOMNamespace.java
DOMRangeSample.java
DOMSample.java
EventSample.java
I18nSafeXMLFileWritingSample.java
NodeIteratorSample.java
ParseXMLFromString.java
TreeWalkerSample.java

DOM APIの使用方法を示します。

  • DOM2Namespace.javaでは、DOMレベル2.0 APIの使用方法が示されます。

  • DOMNamespace.javaでは、DOM APIへの名前空間による拡張の使用方法が示されます。

  • DOMRangeSample.javaでは、DOM範囲APIの使用方法が示されます。

  • DOMSample.javaでは、DOM APIの基本的な使用方法が示されます。

  • EventSample.javaでは、DOM Event APIの使用方法が示されます。

  • NodeIteratorSample.javaでは、DOM Iterator APIの使用方法が示されます。

  • TreeWalkerSample.javaでは、DOM TreeWalker APIの使用方法が示されます。

jaxp

JAXPExamples.java
age.xsl
general.xml
jaxpone.xml
jaxpone.xsl
jaxpthree.xsl
jaxptwo.xsl
oraContentHandler.java

JAXP APIの様々な使用方法を示します。

  • JAXPExamples.javaでは、JAXP 1.1 APIを使用してOracleエンジンを実行する方法の例がいくつか示されます。

  • oraContentHandler.javaでは、SAXコンテンツ・ハンドラが実装されます。このプログラムは、XMLタグを認識する際に、startDocument()endDocument()startElement()endElement()などのメソッドを起動します。

sax

SAX2Namespace.java
SAXNamespace.java
SAXSample.java
Tokenizer.java

SAX APIの様々な使用方法を示します。

  • SAX2Namespace.javaでは、SAX 2.0の使用方法が示されます。

  • SAXNamespace.javaでは、SAX APIへの名前空間による拡張の使用方法が示されます。

  • SAXSample.javaでは、SAX APIの基本的な使用方法が示されます。

  • Tokenizer.javaでは、XMLTokenインタフェースAPIの使用方法が示されます。このプログラムは、XMLTokenインタフェースを実装します。このインタフェースは、setTokenHandler()メソッドを使用して登録する必要があります。XMLTokenのリクエストは、setToken()メソッドを使用して登録します。トークン化実行中、パーサーは、ドキュメントを検証せず、内部および外部のユーティリティの追加または読取りも行いません。

xslt

XSLSample.java
XSLSample2.java
match.xml
match.xsl
math.xml
math.xsl
number.xml
number.xsl
position.xml
position.xsl
reverse.xml
reverse.xsl
string.xml
string.xsl
style.txt
variable.xml
variable.xsl

XSLTを使用した文書の変換を示します。

  • XSLSample.javaでは、Oracle XML ParserのXSL処理機能の使用方法が示されます。与えられた入力スタイルシートを使用して入力XML文書が変換されます。このデモは、XSL変換の結果をDocumentFragmentとして構築するため、xsl:output機能をサポートしません。

  • XSLSample2.javaでは、与えられた入力スタイルシートを使用して入力XML文書が変換されます。このデモは、XSL変換の結果をストリーム化するため、xsl:output機能をサポートします。

関連項目: 「XSLTプロセッサのデモ・プログラムの実行」


サンプル・プログラムのコンパイルおよび実行方法に関するドキュメントは、READMEにあります。基本手順は次のとおりです。

  1. ディレクトリを$ORACLE_HOME/xdk/demo/java/parserディレクトリ(UNIXの場合)または%ORACLE_HOME%\xdk\demo\java\parserディレクトリ(Windowsの場合)に変更します。

  2. 「Java XDK環境の設定」の説明に従って、環境を設定します。

  3. 次の各サブディレクトリに変更し、コマンドラインでmake(UNIXの場合)またはMake.bat(Windowsの場合)を実行します。次に例を示します。

    cd comp;make;cd ..
    cd jaxp;make;cd ..
    cd sax;make;cd ..
    cd dom;make;cd ..
    cd xslt;make;cd ..
    

    makeファイルでは、各ディレクトリ内のソース・コードがコンパイルされ、プログラムが実行され、各プログラムの出力が*.outという拡張子の付いたファイルに書き込まれます。

  4. *.outファイルを表示して、プログラムの出力を参照できます。

XML Parserコマンドライン・ユーティリティの使用

$ORACLE_HOME/bin(UNIXの場合)または%ORACLE_HOME%\bin(Windowsの場合)にあるoraxmlユーティリティは、XML文書を解析するコマンドライン・インタフェースです。XML文書が整形式であるか、および妥当であるかを確認します。

oraxmlを使用するには、次の条件に該当することを確認する必要があります。

  • 「Java XDK環境の設定」の説明に従ってCLASSPATHが設定されていること。特に、環境変数CLASSPATHxmlparserv2.jarファイルを指していることを確認してください。

  • 環境変数PATHが、使用しているJDKのバージョンに付属するJavaインタプリタを検索できること。

表4-4に、oraxmlのコマンドライン・オプションを示します。

表4-4 oraxmlのコマンドライン・オプション

オプション 用途

-help

ヘルプ・メッセージを出力します。

-version

バージョン番号の出力

-novalidate fileName

入力ファイルが整形式かどうかの確認

-dtd fileName

DTD検証を使用した入力ファイルの検証

-schema fileName

スキーマ検証を使用した入力ファイルの検証

-log logfile

出力ログ・ファイルへのエラーの書込み

-comp fileName

入力XMLファイルの圧縮

-decomp fileName

入力圧縮ファイルの解凍

-enc fileName

入力ファイルのエンコーディングの出力

-warning

警告の表示


たとえば、$ORACLE_HOME/xdk/demo/java/parser/commonディレクトリに変更します。コマンドラインで次のコマンドを実行することにより、文書family.xmlfamily.dtdに対して妥当であるかどうかを検証できます。

oraxml -dtd -enc family.xml

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

The encoding of the input file: UTF-8The input XML file is parsed without errors using DTD validation mode.

DOMを使用したXMLの解析

W3C標準ライブラリorg.w3c.domでは、Documentクラスと、DOMのコンポーネント用のクラスが定義されています。Oracle XML Parserには、標準のDOM APIが組み込まれており、W3C DOMの勧告に準拠しています。org.w3c.domとともに、Oracle XML Parsingには、DOM APIを実装するクラスが組み込まれており、これらを拡張して文書フラグメントの出力や名前空間情報の取得などの機能を提供しています。

この項の内容は次のとおりです。

DOM APIの使用

XMLアプリケーションにDOMベースのコンポーネントを実装するには、次のXDKクラスを使用します。

  • oracle.xml.parser.v2.DOMParser。このクラスは、W3C勧告に準拠したXML 1.0のパーサーを実装します。DOMParserXMLParserを拡張するため、XMLParserのすべてのメソッドはDOMParserで使用できます。

  • oracle.xml.parser.v2.XMLDOMImplementation。このクラスにはプラグ可能な拡張性のあるDOMを作成するファクトリ・メソッドが含まれます。

    つまり、XMLDOMImplementationクラスで作成されたDOMがプラグ可能な拡張性のあるDOMです。

DOMNamespaceおよびDOM2Namespaceクラスも利用できます。これらのクラスは、$ORACLE_HOME/xdk/demo/java/parser/domに含まれているサンプル・プログラムです。

DOMパーサーのアーキテクチャ

例4-4に、DOMパーサーのアーキテクチャを示します。

図4-4 DOMパーサーの基本アーキテクチャ

この図については前後のテキストで説明しています。
「図4-4 DOMパーサーの基本アーキテクチャ」の説明

基本的なDOM解析の実行

プログラムDOMSample.javaに、入力XML文書の解析およびDOMを介してのアクセスへの基本手順の説明があります。

プログラムは、入力としてXMLファイルを受け取って解析し、DOMツリー内の要素と属性を出力します。

これらの手順により、使用可能なメソッドおよびインタフェースを提供する表への参照できます。

  1. DOMParser()コンストラクタをコールすることにより、DOMParserオブジェクトを作成します。このパーサーを使用して、入力XMLデータ文書とDTDを解析できます。DOMSample.javaの次のコード部分は、この方法を示します。

    DOMParser parser = new DOMParser();
    
  2. パーサーのプロパティを構成します。表4-5を参照してください。

    DOMSample.javaの次のコード部分は、エラー出力ストリームを指定し、検証モードをDTD検証に設定して、警告メッセージを有効にします。

    parser.setErrorStream(System.err);
    parser.setValidationMode(DOMParser.DTD_VALIDATION);
    parser.showWarnings(true);
    
  3. parse()メソッドを起動することにより、入力XML文書を解析します。このプログラムでは、Nodeオブジェクトのツリーをメモリーに構築します。

    DOMSample.javaのこのコード部分は、java.net.URLクラスのインスタンスを解析する方法を示します。

    parser.parse(url);
    

    XML入力は、ファイル、文字列バッファまたはURLのいずれかになることに注意してください。次のコード部分に示すとおり、DOMSample.javaはファイル名をパラメータとして受け入れ、createURLヘルパー・メソッドをコールして、パーサーに渡すことのできるURLオブジェクトを作成します。

    public class DOMSample
    {
       static public void main(String[] argv)
       {
          try
          {
             if (argv.length != 1)
             {
                // Must pass in the name of the XML file.
                System.err.println("Usage: java DOMSample filename");
                System.exit(1);
             }
             ...
             // Generate a URL from the filename.
             URL url = DemoUtil.createURL(argv[0]);
             ...
    
  4. getDocument()を起動して、パーサーがメモリー内DOMツリーのルートへのハンドルを取得します。このハンドルはXMLDocumentオブジェクトです。このハンドルを使用して、解析されたXML文書のすべての部分にアクセスできます。表4-6に、XMLDocumentクラスが実装するインタフェースを示します。

    DOMSample.javaのこのコード部分は、この方法を示します。

    XMLDocument doc = parser.getDocument();
    
  5. 様々なXMLDocumentメソッドをコールすることにより、取得した文書のDOMノードを取得および操作します。表4-7を参照してください。

    DOMSample.javaの次のコード部分は、DOMParser.print()メソッドを使用して、DOMツリーの要素および属性を出力します。

    System.out.print("The elements are: ");
    printElements(doc);
    
    System.out.println("The attributes of each element are: ");
    printElementAttributes(doc);
    

    プログラムは、printElements()メソッドを実装します。このメソッドは、getElementsByTagName()をコールして、DOMツリー内のすべての要素のリストを取得します。次に、リスト内の各アイテムをループし、getNodeName()をコールして各要素の名前を出力します。

    static void printElements(Document doc)
    {
       NodeList nl = doc.getElementsByTagName("*");
       Node n;
    
       for (int i=0; i<nl.getLength(); i++)
       {
          n = nl.item(i);
          System.out.print(n.getNodeName() + " ");
       }
    
       System.out.println();
    }
    

    プログラムは、printElementAttributes()メソッドを実装します。このメソッドは、Document.getElementsByTagName()をコールして、DOMツリー内のすべての要素のリストを取得します。次に、リスト内の各要素をループし、Element.getAttributes()をコールして要素の属性のリストを取得します。次に、Node.getNodeName()をコールして属性名を取得し、Node.getNodeValue()をコールして属性値を取得します。

    static void printElementAttributes(Document doc)
    {
       NodeList nl = doc.getElementsByTagName("*");
       Element e;
       Node n;
       NamedNodeMap nnm;
    
       String attrname;
       String attrval;
       int i, len;
    
       len = nl.getLength();
    
       for (int j=0; j < len; j++)
       {
          e = (Element)nl.item(j);
          System.out.println(e.getTagName() + ":");
          nnm = e.getAttributes();
    
          if (nnm != null)
          {
             for (i=0; i<nnm.getLength(); i++)
             {
                n = nnm.item(i);
                attrname = n.getNodeName();
                attrval = n.getNodeValue();
                System.out.print(" " + attrname + " = " + attrval);
             }
          }
          System.out.println();
       }
    }
    
  6. reset()メソッドを起動することにより、パーサーの状態をリセットします。これで、パーサーが新規文書を解析する準備ができます。

有用なメソッドおよびインタフェース

次の表に、「基本的なDOM解析の実行」で作成したようなアプリケーションの作成に使用する有用なメソッドおよびインタフェースを示します。

表4-5に、有用な構成メソッドを示します。

表4-5 DOMParser構成メソッド

メソッド メソッドの用途

setBaseURL()

外部エンティティとDTDをロードするためのベースURLを設定します。このメソッドは、XML文書がInputStreamである場合にコールします。

setDoctype()

解析時に使用するDTDを指定します。

setErrorStream()

エラーと警告の出力の出力ストリームを作成します。

setPreserveWhitespace()

入力XML文書に空白を保持するようにパーサーに指示します。

setValidationMode()

パーサーの検証モードを設定します。表4-1に、このメソッドで使用できるフラグを示します。

showWarnings()

パーサーが警告を出力する必要があるかどうかを指定します。


表4-6に、XMLDocumentクラスが実装するインタフェースを示します。

表4-6 XMLDocumentにより実装されるいくつかのインタフェース

インタフェース 定義内容

org.w3c.dom.Node

文書ツリー内の単一ノードと、ノードをアクセスおよび処理するためのメソッド

org.w3c.dom.Document

XML文書全体を表すNode

org.w3c.dom.Element

XML要素を表すNode


表4-7に、ノードを取得するときに役立つメソッドをいくつか示します。

表4-7 有用なXMLDocumentのメソッド

メソッド メソッドの用途

getAttributes()

このノードが要素である場合はその属性を含むNamedNodeMapを生成し、要素でない場合はnullを生成します。

getElementsbyTagName()

特定のレベルの特定のタグ名と一致するすべての要素を再帰的に取得します。このメソッドでは、任意のタグと一致する*タグがサポートされます。文書のルートへのハンドルを通じてgetElementsByTagName("*")をコールして、文書内のすべての要素のリストを生成します。

getExpandedName()

要素の拡張名を取得します。このメソッドは、NSNameインタフェースで指定されます。

getLocalName()

この要素のローカル名を取得します。要素名が<E1:locn xmlns:E1="http://www.oracle.com/"/>の場合は、locnがローカル名です。

getNamespaceURI()

このノードの名前空間URIを取得するか、名前空間URIが指定されていない場合はnullを戻します。要素名が<E1:locn xmlns:E1="http://www.oracle.com/"/>の場合は、http://www.oracle.comが名前空間URIです。

getNodeName()

DOMツリー内のノードの名前を取得します。

getNodeValue()

タイプに応じて、このノードの値を取得します。このモードは、Nodeインタフェースにあります。

getPrefix()

要素の名前空間の接頭辞を取得します。

getQualifiedName()

要素の修飾名を取得します。要素名が<E1:locn xmlns:E1="http://www.oracle.com/"/>の場合は、E1:locnが修飾名です。

getTagName()

DOMツリー内の要素の名前を取得します。


拡張性のあるDOMの作成

この項では、拡張性のあるDOMの作成および使用方法について説明します。

この項の内容は次のとおりです。

プラグ可能なDOMの使用

プラグ可能なDOMはデータからDOM APIを分割します。基礎となるデータは、内部またはプラグインのいずれかで、いずれの場合もバイナリXMLです。

  • 内部データ

    XMLテキストが解析されていない内部データをプラグインするには、XMLテキストはバイナリXMLとして保存され、DOMParserによって解析される必要があります。解析済バイナリXMLは、DOM APIレイヤーのInfoSetReaderにプラグインされます。

    InfosetReader引数は、基礎となるXMLデータへのインタフェースです。

  • プラグイン・データ

    プラグイン・データはすでに解析済のデータなので、プロセッサ間の転送で解析は不要です。

プラグ可能なDOMを作成するには、XMLデータは、たとえばXMLDOMImplementationオブジェクト上のInfosetReaderインタフェースを介してプラグインされます。

public Document createDocument(InfosetReader reader) throws DOMException

InfosetReaderAPIはXDKBinXMLStreamの上位に実装されます。DOM4J、JDOMまたはJDBCなどのXMLデータの他の形式のオプションのアダプタも、サポートされます。ユーザーは独自の実装にもプラグインできます。

InfosetReaderは、拡張性のあるDOM APIレイヤーおよび基礎となるデータ間のインタフェースとして機能します。XMLデータにアクセスするのは、一般的なストリームベースのPull APIです。InfosetReaderは、XMLストリームからシーケンシャルなイベントを取得し、これらのイベントから状態およびデータの問合せを実行します。次の例では、XMLデータがスキャンされ、QNameおよびすべての要素の属性が取得されています。

InfosetReader reader;
While (reader.hasNext())
{
   reader.next();
   if (reader.getEventType() == START_ELEMENT)
   {
        QName name = reader.getQName();
        TypedAttributeList attrList = reader.getAttributeList();
     }
}
InfosetReaderオプション

InfosetReaderインタフェースは次の機能をサポートしています。

コピー: 文書間のDOMのシャドウ・コピーをサポートするには、InfosetReaderの新規コピーを作成して、Cloneメソッドを使用して、スレッドを安全にします。BinXMLStreamから取得されたInfosetReaderは、常にこれをサポートします(オプション)。

フォーカスの移動: 遅延マテリアライズをサポートするには、InfosetReaderは、Offsetによって指定されたどの場所にもフォーカスを移動できます(オプション)。

If (reader.hasSeekSupport())
   reader.seek(offset);
InfosetWriter

InfosetWriterは、データ書込みをサポートするInfosetReaderインタフェースの拡張機能です。XDKでは、バイナリXMLの上位で実装されます。ユーザーはこの実装を変更できません。

XMLテキストのバイナリXMLとしての保存

XMLテキストから拡張性のあるDOMを作成するには、DOMParser実行前に、XMLテキストをバイナリXMLとして保存する必要があります。次のいずれかでもXMLテキストを保存できます。

  • バイナリXML

  • バイナリXMLへの参照: データ・ソースがデシリアライズに使用可能と判明している場合は、現在のデータのかわりにバイナリXMLの参照項目を保存できます。

次の例に、バイナリXMLとしての保存方法を示します。

XMLDocument doc;
InfosetWriter writer;
doc.save(writer, false);
writer.close();

バイナリXMLへの参照として保存するには、saveコマンドに対する引数としてtrueを使用します。

遅延マテリアライズの使用

遅延マテリアライズを使用して、空のDOMをプラグインできます。それにより、必要な場合はより多くのデータを取り出せ、不要になったらノードを解放できます。

オンデマンドでのデータの取出し

プラグインDOMアーキテクチャは、ツリーのルートとして単一のDocumentノードを含む空のDOMを作成します。残りのDOMツリーは、後でアクセスして拡張できます。ノードには拡張されない子や兄弟関係のノードがある場合もありますが、親および祖先は常に拡張されます。DOMはデータを取り出して次のノードを作成できるように、各ノードは次のノードのInfoSetReader.Offsetプロパティを保持します。

アクセス・メソッド・タイプによっては、DOMノードは戻されたノードのセット以上に拡張する場合もあります。

  • DOMナビゲーション

    DOMナビゲーション・インタフェースは、最初の子や、最後の子、親、兄弟関係などの近隣のノードへのアクセスを可能にします。ノード作成が必要な場合は、ドキュメント内の順に行われます。

  • IDの索引付け

    DTDまたはXMLスキーマは、型IDでノードを指定できます。DOMがIDの索引付けをサポートする場合、それらのノードは、索引を使用して直接取出し可能になります。拡張性のあるDOMの場合、索引による取得で、前のすべてのノードが拡張されるわけではありませんが、祖先のノードは生成されます。

  • Xpath式

    XPath評価により、メモリー内のすべての中間ノードが生成されます。たとえば、子孫の軸「//」により全体のサブツリーが拡張されますが、評価後に解放されるものもあります。

不要の際のノードの解放

拡張性のあるDOMは、ノードの手動参照解除または自動参照解除のいずれかをサポートします。

  • 弱参照を使用した自動参照解除

    自動参照解除を有効化するには、PARTIAL_DOM属性をBoolean.TRUEに設定します。

    DOMナビゲーションをサポートしている場合は、ノード間のクロス参照を追加する必要があります。自動参照解除モードでは、リンクのいくつかは弱参照で、不要データ収集時に解放されます。

    リンクの重要性に基づいてノードが解放されます。親ノードへのリンクは削除されません。有効範囲内の名前空間に祖先がコンテキストを提供し、InfosetReaderなどのストリームAPIを使用して削除済の親ノードを取得するのは難しいためです。

    拡張性のあるDOMは、親および兄弟関係を強く保持しますが、子や次のような兄弟関係の保持は弱いです。Java仮想マシンがノードを解放されても、それらへの参照は基礎となるデータで使用可能であるため、必要な場合は再作成できます。

  • 手動参照解除

    手動参照解除を有効化するには、属性PARTIAL_DOMBoolean.FALSEに設定して、プラグインXMLデータを使用してDOMを作成します。

    このモードでは、DOMはアプリケーションに依存し、ツリー全体からドキュメント・フラグメントを明示的に参照解除します。弱参照はありません。アプリケーションに決定的なデータ処理の順序がある場合、ノードの解放と再作成の繰り返し行う余分なオーバーヘッドは回避することをお薦めします。

    すべての他のノードから参照解除を実行するには、freeNode()をコールします。次に例を示します。

    Element root = doc.getDocumentElement();
     Node item = root.getFirstChild();
    While (item != null)
    {
         processItem(item);
         Node tmp = item;
         item = item.getNextSibling();
         ((XMLNode)tmp).freeNode();
    }
    

    拡張性のないDOMではfreeNodeコールは適用されません。

    参照解除するノードはDOMツリーから削除されたノードとは異なることに注意してください。DOMツリーは、freeNodeがDOMノード上でコールされても変更しません。ノードは、その親や兄弟関係からアクセスおよび再作成可能です。ただし、ノード解放後にノードにアクセスする場合は、ノードを保持する変数によってエラーがスローされます。

シャドウ・コピーの使用

シャドウ・コピーを使用して、下部のデータはデータ複製を避けるために共有できます。

XML処理の共通操作であるクローン作成は、プラグ可能なDOMで簡単に実行できます。

copyメソッドが使用されると、コピーされたフラグメントのルート・ノードが作成され、サブツリーはオンデマンドで拡張されます。

データ共有は基礎となるデータに対するもので、DOMノード自体に対してではありません。DOM仕様では、クローンとそのオリジナルではノード識別および親ノードを別のものにする必要があります。

DOM更新の合体

DOM APIは、追加、削除ノード、設定、削除、変更および挿入値などの更新操作をサポートします。DOMがプラグインXMLデータで作成されると、基礎となるデータはDOMの外部とみなされます。DOM更新は、DOM APIから表示可能ですがデータ・ソースは同一のままです。通常の更新操作は使用可能ですが、相互に妨害はしません。

変更済のDOMを永続にするには、DOMを明示的に保存する必要があります。これによりすべての変更はオリジナル・データとともにマージされ、永続格納にデータがシリアライズされます。変更したDOMを明示的に保存しない場合は、トランザクションを終了すると変更が失われます。

PageManagerインタフェースを使用した内部データのサポート

XMLテキストは、DOMParserで解析および構成されて拡張性のあるDOMが作成されると、内部データはバイナリXMLの形式でキャッシュされ、DOM APIレイヤーは内部データの上位で構築されます。バイナリXMLはDOMノードよりさらにコンパクトであるため、これによりスケーラビリティが増大されます。

追加のスケーラビリティに関しては、拡張性のあるDOMはPageManagerインタフェースを介してバイナリ・データのバックエンド格納を使用できます。バイナリ・データは、使用していないメモリーと交換可能です。

このコードの例に、PageManagerインタフェースの使用方法を示します。

DOMParser parser = new DOMParser();
parser.setAttribute(PARTIAL_DOM, Boolean.TRUE); //enable scalable DOM
parser.setAttribute(PAGE_MANAGER, new FilePageManager("pageFile"));
...
// DOMParser other configuration
parser.parse(fileURL);
XMLDocument doc = parser.getDocument();

PageManagerインタフェースが使用されていない場合、パーサーはバイナリXMLとして文書全体をキャッシュします。

構成可能なDOM設定の使用

XMLDOMImplementationクラスを使用してDOMを作成する場合、XMLDOMImplementationクラスのsetAttributeメソッドを使用して、DOMを構成して異なるアプリケーションに適合でき、効率を最大にできます。

public void setAttribute(String name, Object value) throws IllegalArgumentException

拡張性のあるDOMに関しては、PARTIAL_DOMおよびACCESS_MODE属性に対してsetAttributeをコールします。


注意:

新規の属性値が影響するのは次のDOMであり、現在のものではありません。そのため、XMLDOMImplementationのインスタンスは、別の構成でDOMを作成するのに使用されます。

  • PARTIAL_DOM

    この属性は、DOMに拡張性があるかどうか(部分的)、ブール値を取るかどうかを示します。DOM作成は、属性がTRUEに設定されている場合は拡張性があります。必要な場合は、使用されていないノードが解放され、再作成されます。属性がFALSEに設定されている、または設定がない場合はDOM作成に拡張性はありません。

  • ACCESS_MODE

    この属性は、DOMのアクセスを制御し、拡張性のあるDOMおよび拡張性のないDOMの両方に適用します。次の値があります。

    • UPDATEABLE

      DOMはすべてのDOM更新操作をサポートします。

      UPDATEABLEACCESS_MODE属性のデフォルト値で、XDK DOM実装との下位互換性を保持します。

    • READ_ONLY

      DOMはこの読取りのみできます。

      DOMツリーを変更しようとするとエラーが出ますが、新規ノードがDOMツリーに追加されないかぎりクローン作成などのノード作成は可能です。

    • FORWARD_READ

      この値は、getFirstChild().getNextSibling()getLastChild()などのナビゲーションを有効にしますが、getPreviousSibling()などの下位アクセスはできません。

      FORWARD_READは親ノードや祖先ノードにアクセス可能です。

    • STREAMING

      DOMアクセスは、SAXイベント・アクセスに類似したドキュメント内の順での、ノードのストリームに限定されます。

      ストリーム・モードでの現在のノードの概念に従って、現在のノードは、ドキュメント内の順にアクセスした最後のノードです。アプリケーションは変数内にノードを保持し、再度アクセスしますが、DOMメソッドを使用して現在のノードの前に他のノードにアクセスすると、DOMエラーを引き起こします。ただし、祖先のノードおよび属性ノードへのアクセスは常に可能です。

      次に、ストリーム・モードでのDOMの動作を示します。

      Node parent = currentNode.getParentNode(); // OK although parent is before current node
      
      Node child = parent.getFirstChild(); // Error if the current node is not the first child of parent!
      
      Attribute attr = parent.getFirstAttribute();// OK accessing attributes from Element is always //allowed
      

    次に、アクセス・モードを制限の低いものから高いものの順に示します。

    UPDATEABLE > READ_ONLY > FORWARD_READ > STREAM_READ

構成可能なDOM設定へのパフォーマンス上の利点

DOMはREAD_ONLYモードで変更できません。そのため書込みバッファ全体は必要ありません。

DOMはFORWARD_READモードで祖先のノード以外の下位を読み取りません。したがって、兄弟関係のリンクは作成されません。

DOMは親リンクのみ保持し、STREAM_READモードでノードのデータの場所を記憶する必要はありません。したがって、解放されたノードは再作成される必要はありません。

拡張性のあるDOMアプリケーション

ここでは、プラグ可能な拡張性のあるDOMを作成および使用するアプリケーションを示します。

XMLDOMImplementation domimpl = new XMLDOMImplementation();
domimpl.setAttribute(XMLDocument.SCALABLE_DOM, Boolean.TRUE);
domimpl.setAttribute(XMLDocument.ACCESS_MODE,XMLDocument.UPDATEABLE);
XMLDocument scalableDoc = (XMLDocument) domimpl.createDocument(reader);

プラグ可能で、拡張性のある、バイナリXMLに基づくDOMを作成および使用するアプリケーションを次に示します。詳細は第5章「バイナリXML for Javaの使用」を参照してください。

BinXMLProcessor proc = BinXMLProcessorFactory.createProcessor();
BinXMLStream bstr = proc.createBinXMLStream();
BinXMLEncoder enc = bstr.getEncoder();
enc.setProperty(BinXMLEncoder.ENC_SCHEMA_AWARE, false);

SAXParser parser = new SAXParser();
parser.setContentHandler(enc.getContentHandler());
parser.setErrorHandler(enc.getErrorHandler());
parser.parse(BinXMLUtil.createURL(xmlfile));

BinXMLDecoder dec = bstr.getDecoder();
InfosetReader reader = dec.getReader();
XMLDOMImplementation domimpl = new XMLDOMImplementation();
domimpl.setAttribute(XMLDocument.SCALABLE_DOM, Boolean.TRUE);
XMLDocument currentDoc = (XMLDocument) domimpl.createDocument(reader);

名前空間を使用したDOM操作の実行

DOM2Namespace.javaプログラムは、パーサーの単純な使用方法とDOM APIへの名前空間による拡張を示します。プログラムは、XML文書を受け取って解析し、文書内の要素と属性を出力します。

「基本的なDOM解析の実行」の最初の4つの手順(パーサー作成からgetDocument()コールまで)は、基本的にDOM2Namespace.javaの場合と同じです。主な違いは、DOMツリーの出力の手順です(手順5)。DOM2Namespace.javaプログラムでは、かわりに次の処理を行います。

// Print document elements
printElements(doc);

// Print document element attributes
System.out.println("The attributes of each element are: ");
printElementAttributes(doc);

DOM2Namespace.javaにより実装されるprintElements()メソッドは、getElementsByTagName()をコールして、DOMツリー内のすべての要素のリストを取得します。次に、リスト内の各アイテムをループし、各ElementnsElementにキャストします。各nsElementについて、nsElement.getPrefix()をコールして名前空間の接頭辞を取得し、nsElement.getLocalName()をコールしてローカル名を取得し、nsElement.getNamespaceURI()をコールして名前空間URIを取得します。

static void printElements(Document doc)
{
   NodeList nl = doc.getElementsByTagName("*");
   Element nsElement;
   String prefix;
   String localName;
   String nsName;

   System.out.println("The elements are: ");
   for (int i=0; i < nl.getLength(); i++)
   {
      nsElement = (Element)nl.item(i);

      prefix = nsElement.getPrefix();
      System.out.println("  ELEMENT Prefix Name :" + prefix);

      localName = nsElement.getLocalName();
      System.out.println("  ELEMENT Local Name    :" + localName);

      nsName = nsElement.getNamespaceURI();
      System.out.println("  ELEMENT Namespace     :" + nsName);
   }
   System.out.println();
}

printElementAttributes()メソッドは、Document.getElementsByTagName()をコールして、DOMツリー内の要素のNodeListを取得します。次に、各要素をループし、Element.getAttributes()をコールして要素の属性のリストをNamedNodeMapという特殊なリストとして取得します。属性リストの各アイテムについて、nsAttr.getPrefix()をコールして名前空間の接頭辞を取得し、nsAttr.getLocalName()をコールしてローカル名を取得し、nsAttr.getValue()をコールして値を取得します。

static void printElementAttributes(Document doc)
{
   NodeList nl = doc.getElementsByTagName("*");
   Element e;
   Attr nsAttr;
   String attrpfx;
   String attrname;
   String attrval;
   NamedNodeMap nnm;
   int i, len;

   len = nl.getLength();

   for (int j=0; j < len; j++)
   {
      e = (Element) nl.item(j);
      System.out.println(e.getTagName() + ":");

      nnm = e.getAttributes();

      if (nnm != null)
      {
         for (i=0; i < nnm.getLength(); i++)
         {
            nsAttr = (Attr) nnm.item(i);

            attrpfx = nsAttr.getPrefix();
            attrname = nsAttr.getLocalName();
            attrval = nsAttr.getNodeValue();

            System.out.println(" " + attrpfx + ":" + attrname + " = "
                               + attrval);
         }
      }
      System.out.println();
   }
}

イベントを使用したDOM操作の実行

EventSample.javaプログラムは、イベント・リスナーを使用して様々なイベントを登録する方法を示します。たとえば、指定されたDOM要素にノードが追加された場合は、イベントがトリガーされます。これにより、リスナーがイベントに関する情報を出力します。

プログラムは次の手順を実行します。

  1. イベント・リスナーをインスタンス化します。登録された変更によりイベントがトリガーされると、このイベントがイベント・リスナーに渡されて処理されます。EventSample.javaの次のコード部分は、リスナーの実装を示します。

    eventlistener evtlist = new eventlistener();
    ...
    class eventlistener implements EventListener
    {
       public eventlistener(){}
       public void handleEvent(Event e)
       {
          String s = " Event "+e.getType()+" received " + "\n";
          s += " Event is cancelable :"+e.getCancelable()+"\n";
          s += " Event is bubbling event :"+e.getBubbles()+"\n";
          s += " The Target is " + ((Node)(e.getTarget())).getNodeName() + "\n\n";
          System.out.println(s);
       }
    }
    
  2. 新規XMLDocumentをインスタンス化し、getImplementation()をコールしてDOMImplementationオブジェクトを取得します。hasFeature()メソッドをコールして、この実装でサポートされる機能を判断できます。EventSample.javaの次のコード部分は、この方法を示します。

    XMLDocument doc1 = new XMLDocument();
    DOMImplementation impl = doc1.getImplementation();
    
    System.out.println("The impl supports Events "+
                       impl.hasFeature("Events", "2.0"));
    System.out.println("The impl supports Mutation Events "+
                       impl.hasFeature("MutationEvents", "2.0"));
    
  3. 目的のイベントをリスナーに登録します。EventSample.javaの次のコード部分は、文書ノードの3つのイベントを登録します。

    doc1.addEventListener("DOMNodeRemoved", evtlist, false);
    doc1.addEventListener("DOMNodeInserted", evtlist, false);
    doc1.addEventListener("DOMCharacterDataModified", evtlist, false);
    

    EventSample.javaの次のコード部分は、タイプXMLElementのノードを作成し、このノードの3つのイベントを登録します。

    XMLElement el = (XMLElement)doc1.createElement("element");
    ...
    el.addEventListener("DOMNodeRemoved", evtlist, false);
    el.addEventListener("DOMNodeRemovedFromDocument", evtlist, false);
    el.addEventListener("DOMCharacterDataModified", evtlist, false);
    ...
    
  4. イベントをトリガーするアクションを実行します。これらのイベントは、リスナーに渡されて処理されます。EventSample.javaの次のコード部分は、この方法を示します。

    att.setNodeValue("abc");
    el.appendChild(el1);
    el.appendChild(text);
    text.setNodeValue("xyz");
    doc1.removeChild(el);
    

範囲を使用したDOM操作の実行

W3C DOM仕様によると、範囲はDocumentDocumentFragmentまたはAttrのコンテンツの範囲を識別します。範囲の開始と終了に対応する1組の境界点の間にあるコンテンツを選択します。表4-8に、XMLDocumentを介してアクセスできる有用な範囲メソッドを示します。

表4-8 Rangeクラスの有用なメソッド

メソッド 説明

cloneContents()

範囲のコンテンツを複製します。

deleteContents()

範囲のコンテンツを削除します。

getCollapsed()

範囲が閉じている場合にTRUEを戻します。

getEndContainer()

範囲が終了するノードを取得します。

getStartContainer()

範囲が開始するノードを取得します。

selectNode()

ノードとそのコンテンツを選択します。

selectNodeContents()

ノード内のコンテンツを選択します。

setEnd()

範囲の終了を示す属性を設定します。

setStart()

範囲の開始を示す属性を設定します。


DOMRangeSample.javaプログラムは、範囲に対して行える操作をいくつか示します。

「基本的なDOM解析の実行」の最初の4つの手順(パーサー作成からgetDocument()コールまで)は、DOMRangeSample.javaの場合と同じです。DOMRangeSample.javaプログラムは、次の手順に従って進みます。

  1. getDocument()をコールしてXMLDocumentを作成した後で、createRange()で範囲オブジェクトを作成し、setStart()およびsetEnd()をコールしてその境界を設定します。DOMRangeSample.javaの次のコード部分は、この方法を示します。

    XMLDocument doc = parser.getDocument();
    ...
    Range r = (Range) doc.createRange();
    XMLNode c = (XMLNode) doc.getDocumentElement();
    
    // set the boundaries
    r.setStart(c,0);
    r.setEnd(c,1);
    
  2. XMLDocumentメソッドをコールして範囲に関する情報を取得し、そのコンテンツを操作します。表4-8に、有用なメソッドを示します。DOMRangeSample.javaの次のコード部分は、現在のノードのコンテンツを選択して出力します。

    r.selectNodeContents(c);
    System.out.println(r.toString());
    

    次のコード部分は、範囲のコンテンツのクローンを作成して出力します。

    XMLDocumentFragment df =(XMLDocumentFragment) r.cloneContents();
    df.print(System.out);
    

    次のコード部分は、範囲の開始コンテナと終了コンテナを取得して出力します。

    c = (XMLNode) r.getStartContainer();
    System.out.println(c.getText());
    c = (XMLNode) r.getEndContainer();
    System.out.println(c.getText());
    

この項では、デモ・プログラムの一部の機能のみを説明しています。詳細は、デモ・プログラム自体を参照してください。

TreeWalkerを使用したDOM操作の実行

W3C DOMレベル2 Traversal and Range仕様は、NodeFilterおよびTreeWalkerインタフェースを定義しています。XDKには次に示すインタフェースの実装が含まれます。

ノード・フィルタは、特定のタイプのNodeオブジェクトをフィルタ処理するオブジェクトです。たとえば、エンティティ参照ノードをフィルタ処理によって除外し、要素ノードと属性ノードを受け入れることができます。NodeFilterインタフェースを実装し、次にNodeオブジェクトをacceptNode()メソッドに渡すことにより、ノード・フィルタを作成します。通常、acceptNode()メソッド実装はgetNodeType()をコールしてノードのタイプを取得し、それをELEMENT_TYPEATTRIBUTE_TYPEなどの静的変数と比較してから、結果に基づいて表4-9の静的フィールドの1つを戻します。

表4-9 NodeFilterインタフェースの静的フィールド

メソッド 説明

FILTER_ACCEPT

ノードを受け入れます。NodeIteratorまたはTreeWalkerで定義されているナビゲーション・メソッドは、このノードを戻します。

FILTER_REJECT

ノードを拒否します。NodeIteratorまたはTreeWalkerに定義されたナビゲーション・メソッドはこのノードを戻しません。TreeWalkerの場合は、このノードの子も拒否されます。NodeIteratorsは、これをFILTER_SKIPのシノニムとして扱います。

FILTER_SKIP

この単一ノードをスキップします。NodeIteratorまたはTreeWalkerに定義されたナビゲーション・メソッドはこのノードを戻しません。NodeIteratorTreeWalkerの両方で、このノードの子が考慮されます。


TreeWalkerオブジェクトを使用し、whatToShowフラグおよびフィルタ(存在する場合)で定義されている文書のビューを使用して文書ツリーまたはサブツリーを検索できます。XMLDocument.createTreeWalker()メソッドを使用して、次の項目を指定することによりTreeWalkerオブジェクトを作成できます。

  • ツリーのルート・ノード

  • 論理ビューに含める必要のあるノードのタイプを管理するフラグ

  • ノードをフィルタ処理するためのフィルタ

  • エンティティ参照とその子孫を含める必要があるかどうかを決定するフラグ

表4-10に、org.w3c.dom.traversal.TreeWalkerインタフェースの有用なメソッドを示します。

表4-10 TreeWalkerインタフェースの有用なメソッド

メソッド 説明

firstChild()

ツリー・ウォーカーを現在のノードの最初に表示可能な子に移動し、新規ノードを戻します。現在のノードに表示可能な子がない場合は、nullを戻し、現在のノードを保持します。

getRoot()

作成時の指定に従って、ツリー・ウォーカーのルート・ノードを取得します。

lastChild()

ツリー・ウォーカーを現在のノードの最後に表示可能な子に移動し、新規ノードを戻します。現在のノードに表示可能な子がない場合は、nullを戻し、現在のノードを保持します。

nextNode()

ツリー・ウォーカーを文書内の現在のノードとの相対順序で次に表示可能な子に移動し、新規ノードを戻します。


TreeWalkerSample.javaプログラムは、ノード・フィルタとツリー検索で行える操作をいくつか示します。

「基本的なDOM解析の実行」の最初の4つの手順(パーサー作成からgetDocument()コールまで)は、TreeWalkerSample.javaの場合と同じです。TreeWalkerSample.javaプログラムは、次の手順に従って進みます。

  1. ノード・フィルタ・オブジェクトを作成します。NodeFilterインタフェースを実装するnfクラスのacceptNode()メソッドは、getNodeType()を起動してノードのタイプを取得します。TreeWalkerSample.javaの次のコード部分は、この方法を示します。

    NodeFilter n2 = new nf();
    ...
    class nf implements NodeFilter
    {
      public short acceptNode(Node node)
      {
        short type = node.getNodeType();
    
        if ((type == Node.ELEMENT_NODE) || (type == Node.ATTRIBUTE_NODE))
           return FILTER_ACCEPT;
        if ((type == Node.ENTITY_REFERENCE_NODE))
           return FILTER_REJECT;
        return FILTER_SKIP;
      }
    }
    
  2. XMLDocument.createTreeWalker()メソッドを起動してツリー・ウォーカーを作成します。TreeWalkerSample.javaの次のコード部分は、XMLDocumentのルート・ノードをツリー・ウォーカーのルート・ノードとして使用し、ツリー内のすべてのノードを含めます。

    XMLDocument doc = parser.getDocument();
    ...
    TreeWalker tw = doc.createTreeWalker(doc.getDocumentElement(),NodeFilter.SHOW_ALL,n2,true);
    
  3. TreeWalkerオブジェクトのルート要素を取得します。次のコード部分は、この方法を示します。

    XMLNode nn = (XMLNode)tw.getRoot();
    
  4. ツリーを検索します。次のコード部分は、TreeWalker.nextNode()メソッドをコールすることで文書内の順序に従ってツリーを移動する方法を示します。

    while (nn != null)
    {
      System.out.println(nn.getNodeName() + " " + nn.getNodeValue());
      nn = (XMLNode)tw.nextNode();
    }
    

    次のコード部分は、firstChild()メソッドをコールすることでツリーの左側を検索する方法を示します(lastChild()メソッドをコールすることで、ツリーの右側を検索できます)。

     while (nn != null)
     {
       System.out.println(nn.getNodeName() + " " + nn.getNodeValue());
       nn = (XMLNode)tw.firstChild();
     }
    

この項では、デモ・プログラムの一部の機能のみを説明しています。詳細は、デモ・プログラム自体を参照してください。

SAXを使用したXMLの解析

SAXは、イベントベースのXML解析用の標準インタフェースです。この項の内容は次のとおりです。

SAX APIの使用

レベル1およびレベル2のバージョンでリリースされたSAX APIは、インタフェースとクラスの集合です。APIは次のカテゴリに分類できます。

  • Oracle XML Parserで実装されているインタフェース。

  • アプリケーションに実装する必要のあるインタフェース。表4-11に、SAX 2.0のインタフェースを示します。

    表4-11 SAX2ハンドラ・インタフェース

    インタフェース 説明

    ContentHandler

    XMLパーサーから通知を受信します。XMLタグを認識する際の主要なイベント処理メソッドは、startDocument()endDocument()startElement()およびendElement()です。このインタフェースは、メソッドcharacters()およびprocessingInstruction()も定義します。これらのメソッドは、パーサーがXML要素またはインライン処理命令内でテキストを検出した場合に起動されます。

    DeclHandler

    XML文書内のDTD宣言に関する通知を受信します。

    DTDHandler

    通知および未解析(バイナリ)エンティティを処理します。

    EntityResolver

    文書内のURIのリダイレクションの実行に必要です。resolveEntity()メソッドは、パーサーがURIで示されたデータを識別する必要がある場合に起動されます。

    ErrorHandler

    パーサー・エラーを処理します。プログラムは、様々な解析エラーに対してメソッドerror()fatalError()およびwarning()を起動します。

    LexicalHandler

    コメントやCDATAセクション境界などの字句情報に関する通知を受信します。


  • 標準SAXクラス。

  • org.xml.sax.helper内のその他のJavaクラス。SAX 2.0ヘルパー・クラスは次のとおりです。

    • AttributeImplは、AttributeListの永続コピーを作成します。

    • DefaultHandlerは、表4-11に示したSAX2ハンドラ・インタフェースをデフォルトで実装したベース・クラスです。

    • LocatorImplは、解析中の指定されたポイントでロケータの値の永続スナップショットを作成します。

    • NamespaceSupportは、XML名前空間のサポートを追加します。

    • XMLFilterImplは、イベントのストリームを変更する必要のあるアプリケーションで使用されるベース・クラスです。

    • XMLReaderFactoryは、SAXパーサーの動的なロードをサポートします。

  • nulパッケージ内のデモンストレーション・クラス。

図4-5に、SAXパーサーの作成方法と、そのパーサーを使用して入力文書を解析する方法を示します。

図4-5 SAXParserクラスの使用

この図については前後のテキストで説明しています。
「図4-5 SAXParserクラスの使用」の説明

SAXを使用した入力XML文書の解析の基本ステージは次のとおりです。

  1. SAXParserオブジェクトを作成し、そのプロパティを構成します(有用なプロパティ・メソッドは、表4-5を参照)。たとえば、パーサーの検証モードを設定します。

  2. イベント・ハンドラをインスタンス化します。プログラムでは、表4-11のハンドラ・インタフェースの実装を提供する必要があります。

  3. イベント・ハンドラをパーサーに登録します。イベント・ハンドラをパーサーに登録し、特定のイベントの発生時に起動するメソッドをパーサーが認識できるようにする必要があります。表4-12に、SAXParserで使用できる登録メソッドを示します。

    表4-12 イベント・ハンドラを登録するためのSAXParserのメソッド

    メソッド メソッドの用途

    setContentHandler()

    コンテンツ・イベント・ハンドラをアプリケーションに登録します。org.xml.sax.DefaultHandlerクラスはorg.xml.sax.ContentHandlerインタフェースを実装します。アプリケーションでは、解析の途中で新規または別のハンドラを登録できます。SAXパーサーはただちに新規ハンドラの使用を開始します。

    setDTDHandler()

    DTDイベント・ハンドラを登録します。アプリケーションでDTDハンドラを登録しない場合、SAXパーサーにより報告されるすべてのDTDイベントは無視され、警告は出力されません。アプリケーションでは、解析の途中で新規または別のハンドラを登録できます。SAXパーサーはただちに新規ハンドラの使用を開始します。

    setErrorHandler()

    エラー・イベント・ハンドラをアプリケーションに登録します。アプリケーションでエラー・ハンドラを登録しない場合、SAXパーサーにより報告されるすべてのエラー・イベントは無視され、警告は出力されません。ただし、正常な処理が続行されない場合があります。予期しない不具合を回避するために、すべてのSAXアプリケーションでエラー・ハンドラを実装することを強くお薦めします。アプリケーションでは、解析の途中で新規または別のハンドラを登録できます。SAXパーサーはただちに新規ハンドラの使用を開始します。

    setEntityResolver()

    エンティティ・リゾルバをアプリケーションに登録します。アプリケーションでエンティティ・リゾルバを登録しない場合、XMLReaderは独自のデフォルトの解決を実行します。アプリケーションでは、解析の途中で新規または別のリゾルバを登録できます。SAXパーサーはただちに新規リゾルバの使用を開始します。


  4. SAXParser.parse()メソッドを使用して入力文書を解析します。すべてのSAXインタフェースは、同期操作を前提としています。そのため、解析メソッドは解析が完了するまで結果を戻しません。リーダーは、次のイベントを通知する前にイベント・ハンドラのコールバックが結果を戻すまで待機する必要があります。

  5. SAXParser.parse()メソッドがコールされた場合、プログラムでは、アプリケーションで実装された複数のコールバック・メソッドの1つを起動します。メソッドは、イベント・ハンドラで実装されたContentHandlerErrorHandlerDTDHandlerおよびEntityResolverインタフェースにより定義されます。たとえば、アプリケーションは、開始要素を検出した際にstartElement()メソッドをコールできます。

基本的なSAX解析の実行

SAXSample.javaプログラムは、SAX解析の基本手順を示します。SAXSampleクラスはHandlerBaseを拡張します。プログラムは、入力としてXMLファイルを受け取って解析し、ファイルのコンテンツに関する情報を出力します。

プログラムは次の手順を実行します。

  1. Locatorを格納します。Locatorは、SAXイベントを文書の場所に関連付けます。SAXパーサーは、Locatorインスタンスをコンテンツ・ハンドラのsetDocumentLocator()メソッドに渡すことにより、アプリケーションに場所情報を提供します。アプリケーションでは、オブジェクトを使用して、XMLソース文書内の他の任意のコンテンツ・ハンドラ・イベントの場所を取得できます。SAXSample.javaの次のコード部分は、この方法を示します。

    Locator locator;
    
  2. 新規イベント・ハンドラをインスタンス化します。SAXSample.javaの次のコード部分は、この方法を示します。

    SAXSample sample = new SAXSample();
    
  3. SAXパーサーをインスタンス化し、構成します。SAXSample.javaの次のコード部分は、モードをDTD検証に設定します。

    Parser parser = new SAXParser();
    ((SAXParser)parser).setValidationMode(SAXParser.DTD_VALIDATION);
    
  4. SAXパーサーにイベント・ハンドラを登録します。SAXParserクラスの登録メソッドを使用できますが、ハンドラ・インタフェースはユーザーが実装する必要があります。次のコード部分は、ハンドラを登録します。

    parser.setDocumentHandler(sample);
    parser.setEntityResolver(sample);
    parser.setDTDHandler(sample);
    parser.setErrorHandler(sample);
    

    次のコードは、DocumentHandlerインタフェースの実装をいくつか示します。

    public void setDocumentLocator (Locator locator)
    {
      System.out.println("SetDocumentLocator:");
      this.locator = locator;
    }
    public void startDocument()
    {
      System.out.println("StartDocument");
    }
    public void endDocument() throws SAXException
    {
      System.out.println("EndDocument");
    }
    public void startElement(String name, AttributeList atts)
                                                   throws SAXException
    {
      System.out.println("StartElement:"+name);
      for (int i=0;i<atts.getLength();i++)
      {
        String aname = atts.getName(i);
        String type = atts.getType(i);
        String value = atts.getValue(i);
        System.out.println("   "+aname+"("+type+")"+"="+value);
      }
    }
    ...
    

    次のコードは、EntityResolverインタフェースの実装を示します。

    public InputSource resolveEntity (String publicId, String systemId)
                          throws SAXException
    {
      System.out.println("ResolveEntity:"+publicId+" "+systemId);
      System.out.println("Locator:"+locator.getPublicId()+" locator.getSystemId()+
                        " "+locator.getLineNumber()+" "+locator.getColumnNumber());
      return null;
    }
    

    次のコードは、DTDHandlerインタフェースの実装を示します。

    public void notationDecl (String name, String publicId, String systemId)
    {
      System.out.println("NotationDecl:"+name+" "+publicId+" "+systemId);
    }
    public void unparsedEntityDecl (String name, String publicId,
                                    String systemId, String notationName)
    {
      System.out.println("UnparsedEntityDecl:"+name + " "+publicId+" "+
                          systemId+" "+notationName);
    }
    

    次のコードは、ErrorHandlerインタフェースの実装を示します。

    public void warning (SAXParseException e)
               throws SAXException
    {
      System.out.println("Warning:"+e.getMessage());
    }
    public void error (SAXParseException e)
               throws SAXException
    {
      throw new SAXException(e.getMessage());
    }
    public void fatalError (SAXParseException e)
              throws SAXException
    {
      System.out.println("Fatal error");
      throw new SAXException(e.getMessage());
    }
    
  5. 入力XML文書を解析します。次のコード部分は、文書をURLに変換して解析します。

    parser.parse(DemoUtil.createURL(argv[0]).toString());
    

名前空間を使用した基本的なSAX解析の実行

この項では、SAX2Namespace.javaサンプル・プログラムについて説明します。このサンプル・プログラムは、XMLDefaultHandlerというイベント・ハンドラをorg.xml.sax.helpers.DefaultHandlerクラスのサブクラスとして実装します。ContentHandlerインタフェースを実装する最も簡単な方法は、org.xml.sax.helpers.DefaultHandlerクラスを拡張することです。DefaultHandlerクラスは、イベントを処理するためのデフォルトの動作をいくつか提供しますが、一般的な動作は何も行わないことです。

SAX2Namespace.javaプログラムは、対象のイベントに対してのみメソッドをオーバーライドします。具体的には、XMLDefaultHandlerクラスはstartElement()およびendElement()という2つのメソッドのみ実装します。startElementイベントは、SAXParserがXML文書内に新規要素を検出するたびにトリガーされます。このイベントがトリガーされると、startElement()メソッドは要素の名前空間情報を出力します。

SAX2Namespace.javaサンプル・プログラムは次の手順を実行します。

  1. DefaultHandler型の新規イベント・ハンドラをインスタンス化します。次のコード部分は、この方法を示します。

    DefaultHandler defHandler = new XMLDefaultHandler();
    
  2. SAXパーサーを作成し、その検証モードを設定します。SAXSample.javaの次のコード部分は、モードをDTD検証に設定します。

    Parser parser = new SAXParser();
    ((SAXParser)parser).setValidationMode(SAXParser.DTD_VALIDATION);
    
  3. SAXパーサーにイベント・ハンドラを登録します。次のコード部分は、入力文書、DTD、エンティティおよびエラーのハンドラを登録します。

    parser.setContentHandler(defHandler);
    parser.setEntityResolver(defHandler);
    parser.setDTDHandler(defHandler);
    parser.setErrorHandler(defHandler);
    

    次のコードは、XMLDefaultHandler実装を示します。startElement()およびendElement()メソッドは、各要素の修飾名、ローカル名および名前空間URIを出力します(これらの用語の説明は、表4-7を参照)。

    class XMLDefaultHandler extends DefaultHandler
    {
       public void XMLDefaultHandler(){}
       public void startElement(String uri, String localName,
                                String qName, Attributes atts)
       throws SAXException
       {
          System.out.println("ELEMENT Qualified Name:" + qName);
          System.out.println("ELEMENT Local Name    :" + localName);
          System.out.println("ELEMENT Namespace     :" + uri);
    
          for (int i=0; i<atts.getLength(); i++)
          {
             qName = atts.getQName(i);
             localName = atts.getLocalName(i);
             uri = atts.getURI(i);
    
             System.out.println(" ATTRIBUTE Qualified Name   :" + qName);
             System.out.println(" ATTRIBUTE Local Name       :" + localName);
             System.out.println(" ATTRIBUTE Namespace        :" + uri);
    
             // You can get the type and value of the attributes either
             // by index or by the Qualified Name.
    
             String type = atts.getType(qName);
             String value = atts.getValue(qName);
    
             System.out.println(" ATTRIBUTE Type             :" + type);
             System.out.println(" ATTRIBUTE Value            :" + value);
    
             System.out.println();
    
          }
       }
       public void endElement(String uri, String localName,
                              String qName) throws SAXException
       {
          System.out.println("ELEMENT Qualified Name:" + qName);
          System.out.println("ELEMENT Local Name    :" + localName);
          System.out.println("ELEMENT Namespace     :" + uri);
       }
    }
    
  4. 入力XML文書を解析します。次のコード部分は、文書をURLに変換して解析します。

    parser.parse(DemoUtil.createURL(argv[0]).toString());
    

XMLTokenizerを使用したSAX解析の実行

単純なSAXパーサーをXMLTokenizerクラスのインスタンスとして作成し、パーサーを使用して入力XMLをトークン化できます。表4-13に、このクラスの有用なメソッドを示します。

表4-13 XMLTokenizerのメソッド

メソッド 説明

setToken()

XMLトークン化機能の新規トークンを登録します。

setErrorStream()

エラーの出力ストリームを登録します。

tokenize()

入力XMLをトークン化します。


Tokenizer機能のあるSAXパーサーは、XMLTokenインタフェースを実装します。XMLTokenのコールバック・メソッドはtoken()です。このメソッドは、XMLトークンおよび対応する値を受け取り、アクションを実行します。たとえば、トークン名に続いてトークンの値を出力するようにtoken()を実装できます。

Tokenizer.javaプログラムは、入力としてXML文書を受け取って解析し、XMLトークンのリストを出力します。プログラムでは、次の処理を行うdoParse()メソッドを実装します。

  1. 入力XMLストリームからURLを作成します。

    URL url = DemoUtil.createURL(arg);
    
  2. 次のようにXMLTokenizerパーサーを作成します。

    parser  = new XMLTokenizer ((XMLToken)new Tokenizer());
    
  3. 次のように出力エラー・ストリームを登録します。

    parser.setErrorStream  (System.out);
    
  4. トークンをパーサーに登録します。Tokenizer.javaの次のコード部分は、登録されたトークンをいくつか示します。

    parser.setToken (STagName, true);
    parser.setToken (EmptyElemTag, true);
    parser.setToken (STag, true);
    parser.setToken (ETag, true);
    parser.setToken (ETagName, true);
    ...
    
  5. XML文書を次のようにトークン化します。

    parser.tokenize (url);
    

    token()コールバック・メソッドは、特定のトークンが検出されたときに実行するアクションを判断します。Tokenizer.javaの次のコード部分は、このメソッドの実装の一部を示します。

    public void token (int token, String value)
    {
       switch (token)
       {
       case XMLToken.STag:
          System.out.println ("STag: " + value);
          break;
       case XMLToken.ETag:
          System.out.println ("ETag: " + value);
          break;
       case XMLToken.EmptyElemTag:
          System.out.println ("EmptyElemTag: " + value);
          break;
       case XMLToken.AttValue:
          System.out.println ("AttValue: " + value);
          break;
       ...
       default:
          break;
       }
    }
    

JAXPを使用したXMLの解析

JAXPを使用すると、JavaプログラムでSAXおよびDOMパーサーとXSLTプロセッサを使用できます。この項の内容は次のとおりです。

JAXP APIの使用

表4-14に示すJAXP APIは、パーサーをプラッガブルにするためのシン・レイヤーを提供する抽象クラスで構成されたAPI構造を持ちます。Oracleは、Sun社のリファレンス実装に基づいてJAXPを実装しています。

表4-14 JAXPパッケージ

パッケージ 説明

javax.xml.parsers

DOM 2.0およびSAX 1.0パーサーの標準APIを提供します。パッケージには、SAXParserおよびDocumentBuilderを提供する、ベンダーに依存しないファクトリ・クラスが含まれています。DocumentBuilderは、DOM準拠のDocumentオブジェクトを作成します。

javax.xml.transform

XML変換の処理と、ソースから結果への変換の実行のための汎用APIを定義します。

javax.xml.transform.dom

DOM固有の変換APIを提供します。

javax.xml.transform.sax

SAX2固有の変換APIを提供します。

javax.xml.transform.stream

ストリーム固有およびURI固有の変換APIを提供します。


JAXPを介したSAX APIの使用

ファクトリ設計パターンに依存して、JAXPを使用して新規SAXパーサー・エンジンを作成できます。図4-6に、基本プロセスを示します。

図4-6 JAXPを使用したSAX解析

次のテキストで説明します。
「図4-6 JAXPを使用したSAX解析」の説明

JAXPを介したSAXでの解析の基本手順は、次のとおりです。

  1. SAXParserFactoryクラスで新規SAXパーサー・ファクトリを作成します。

  2. ファクトリを構成します。

  3. 新規SAXパーサー(SAXParser)オブジェクトをファクトリから作成します。

  4. SAXパーサーのイベント・ハンドラを設定します。

  5. 入力XML文書を解析します。

JAXPを介したDOM APIの使用

ファクトリ設計パターンに基づき、JAXPを使用して新規DOM文書ビルダー・エンジンを作成できます。図4-7に、基本プロセスを示します。

図4-7 JAXPを使用したDOM解析

JAXPを使用したDOM解析の基本プロセスを示します。
「図4-7 JAXPを使用したDOM解析」の説明

JAXPを介したDOMでの解析の基本手順は、次のとおりです。

  1. DocumentBuilderFactoryクラスで新規DOMパーサー・ファクトリを作成します。

  2. ファクトリを構成します。

  3. 新規DOMビルダー(DocumentBuilder)オブジェクトをファクトリから作成します。

  4. DOMビルダーのエラー・ハンドラとエンティティ・リゾルバを設定します。

  5. 入力XML文書を解析します。

JAXPを介したXMLの変換

JAXPを介したXML変換の基本手順は、次のとおりです。

  1. 新規トランスフォーマ・ファクトリを作成します。TransformerFactoryクラスを使用します。

  2. ファクトリを構成します。

  3. ファクトリから新規トランスフォーマを作成し、XSLTスタイルシートを指定します。

  4. トランスフォーマを構成します。

  5. 文書を変換します。

JAXPを使用した解析

JAXPExamples.javaプログラムは、JAXPを使用した解析の基本手順を示します。このプログラムは、次のメソッドを実装し、これらのメソッドを使用して/jaxpディレクトリ内のXMLファイルに対して解析および追加処理を実行します。

  • basic()

  • identity()

  • namespaceURI()

  • templatesHandler()

  • contentHandler2contentHandler()

  • contentHandler2DOM()

  • reader()

  • xmlFilter()

  • xmlFilterChain()

プログラムでは、サンプルXMLファイルjaxpone.xmlおよびjaxpone.xslのURLを作成し、前述のメソッドを順番にコールします。デモの基本設計は次のとおりです(スペースを節約するために、basic()メソッドのみ示しています)。

public class JAXPExamples
{
        public static void main(String argv[])
        throws TransformerException, TransformerConfigurationException,
               IOException, SAXException, ParserConfigurationException,
               FileNotFoundException
        {
        try {
         URL xmlURL = createURL("jaxpone.xml");
         String xmlID = xmlURL.toString();
         URL xslURL = createURL("jaxpone.xsl");
         String xslID = xslURL.toString();
         //
         System.out.println("--- basic ---");
         basic(xmlID, xslID);
         System.out.println();
         ...
      } catch(Exception err) {
        err.printStackTrace();
      }
   }
   //
   public static void basic(String xmlID, String xslID)
      throws TransformerException, TransformerConfigurationException
   {
      TransformerFactory tfactory = TransformerFactory.newInstance();
      Transformer transformer = tfactory.newTransformer(new StreamSource(xslID));
      StreamSource source = new StreamSource(xmlID);
      transformer.transform(source, new StreamResult(System.out));
   }
...
}

JAXPExamples.javaプログラムのreader()メソッドは、SAXを使用してXML文書を解析するための簡単な方法を示します。このメソッドは次の手順を実行します。

  1. TransformerFactoryの新規インスタンスを作成し、SAXTransformerFactoryにキャストします。アプリケーションでは、SAXファクトリを使用して、SAXパーサー・インスタンスを構成および取得できます。次に例を示します。

    TransformerFactory tfactory = TransformerFactory.newInstance();
    SAXTransformerFactory stfactory = (SAXTransformerFactory)tfactory;
    
  2. スタイルシートからStreamSourceオブジェクトを作成し、それをファクトリ・メソッドnewXMLFilter()に渡すことにより、XMLリーダーを作成します。このメソッドは、指定されたSourceを変換命令として使用するXMLFilterオブジェクトを戻します。次に例を示します。

    URL xslURL = createURL("jaxpone.xsl");
    String xslID = xslURL.toString();
    ...
    StreamSource streamSource = new StreamSource(xslID);
    XMLReader reader = stfactory.newXMLFilter(streamSource);
    
  3. コンテンツ・ハンドラを作成し、XMLリーダーに登録します。次の例では、クラスoraContentHandlerのインスタンスを作成します。このインスタンスは、デモ・ディレクトリ内のoraContentHandler.javaプログラムをコンパイルすることで作成されます。

    ContentHandler contentHandler = new oraContentHandler();
    reader.setContentHandler(contentHandler);
    

    次のコード部分は、oraContentHandlerクラスの実装の一部を示します。

    public class oraContentHandler implements ContentHandler
    {
       private static final String TRADE_MARK = "Oracle 9i ";
    
       public void setDocumentLocator(Locator locator)
       {
          System.out.println(TRADE_MARK + "- setDocumentLocator");
       }
    
       public void startDocument()
          throws SAXException
       {
          System.out.println(TRADE_MARK + "- startDocument");
       }
    
       public void endDocument()
          throws SAXException
       {
          System.out.println(TRADE_MARK + "- endDocument");
       }
       ...
    
  4. InputSourceXMLReader.parse()メソッドに渡すことにより、入力XML文書を解析します。次に例を示します。

    InputSource is = new InputSource(xmlID);
    reader.parse(is);
    

JAXPを使用した基本的な変換の実行

JAXPを使用して、インタフェースSourceの任意のクラスをインタフェースResultのクラスに変換できます。表4-15に、いくつかの変換例を示します。

表4-15 JAXPを使用したクラスの変換

JAXPを使用して変換するクラス 変換先クラス

DOMSource

DOMResult

StreamSource

StreamResult

SAXSource

SAXResult


これらの変換は、次のタイプの入力を受け入れます。

  • XML文書

  • スタイルシート

  • oraContentHandler.javaで定義されたContentHandlerクラス

たとえば、identity()メソッドを使用して、出力XML文書が入力XML文書と同じになる変換を実行できます。xmlFilterChain()メソッドを使用して、連鎖内の3つのスタイルシートを適用できます。

basic()メソッドは、基本的なXSLT変換の実行方法を示します。メソッドは次の手順を実行します。

  1. TransformerFactoryの新規インスタンスを作成します。次に例を示します。

    TransformerFactory tfactory = TransformerFactory.newInstance();
    
  2. ファクトリから新規XSLトランスフォーマを作成し、変換に使用するスタイルシートを指定します。次の例では、jaxpone.xslスタイルシートを指定します。

    URL xslURL = createURL("jaxpone.xsl");
    String xslID = xslURL.toString();
    . . .
    Transformer transformer = tfactory.newTransformer(new StreamSource(xslID));
    
  3. ストリーム・ソースを入力XML文書に設定します。basic()メソッドの次の部分は、ストリーム・ソースをjaxpone.xmlに設定します。

    URL xmlURL = createURL("jaxpone.xml");
    String xmlID = xmlURL.toString();
    . . .
    StreamSource source = new StreamSource(xmlID);
    
  4. StreamSourceの文書をStreamResultに変換します。次の例では、StreamSourceStreamResultに変換します。

    transformer.transform(source, new StreamResult(System.out));
    

XMLの圧縮

Oracle XDKでは、SAXまたはDOMを使用してXMLを解析し、解析したデータを圧縮バイナリ・ストリームに書き込むことができます。次に、プロセスを反転し、XMLデータを再構築できます。この項の内容は次のとおりです。

DOMからXMLへの圧縮と解凍

DOMCompression.javaおよびDOMDeCompression.javaプログラムは、DOMの圧縮と解凍の基本手順を示します。最も重要なDOM圧縮メソッドは次のとおりです。

  • XMLDocument.writeExternal()は、オブジェクトに関する情報でバイナリ圧縮ストリームを作成することにより、オブジェクトの状態を保存します。

  • XMLDocument.readExternal()は、writeExternal()メソッドにより圧縮ストリームに書き込まれた情報を読み取り、オブジェクトを復元します。

DOMオブジェクトの圧縮

シリアライズの基本的な方法は、XML文書を解析してXMLDocumentを作成し、ObjectOutputStreamを初期化し、さらにXMLDocument.writeExternal()をコールして圧縮ストリームを書き込むことです。

DOMCompression.javaプログラムは次の手順を実行します。

  1. DOMパーサーを作成し、入力XML文書を解析し、DOM表現を取得します。この方法は、「基本的なDOM解析の実行」で説明しています。DOMCompression.javaの次のコード部分は、この方法を示します。

    public class DOMCompression
    {
       static OutputStream out = System.out;
       public static void main(String[] args)
       {
          XMLDocument doc = new XMLDocument();
          DOMParser parser = new DOMParser();
          try
          {
            parser.setValidationMode(XMLParser.SCHEMA_VALIDATION);
            parser.setPreserveWhitespace(false);
            parser.retainCDATASection(true);
            parser.parse(createURL(args[0]));
            doc = parser.getDocument();
            ...
    
  2. FileOutputStreamを作成し、シリアライズのためにObjectOutputStreamにラップします。次のコード部分は、xml.ser出力ファイルを作成します。

    OutputStream os = new FileOutputStream("xml.ser");
    ObjectOutputStream oos = new ObjectOutputStream(os);
    
  3. XMLDocument.writeExternal()をコールして、オブジェクトをファイルにシリアライズします。このメソッドは、このオブジェクトに関する情報でバイナリ圧縮ストリームを作成することにより、オブジェクトの状態を保存します。この方法を示す文は次のとおりです。

    doc.writeExternal(oos);
    

DOMオブジェクトの解凍

解凍の基本的な方法は、ObjectInputStreamオブジェクトを作成し、XMLDocument.readExternal()をコールして圧縮ストリームを読み取ることです。DOMDeCompression.javaプログラムは次の手順を実行します。

  1. 圧縮ファイルのファイル入力ストリームを作成し、ObjectInputStreamにラップします。DOMDeCompression.javaの次のコード部分は、前の項で作成した圧縮ファイルからFileInputStreamを作成します。

    InputStream is;
    ObjectInputStream ois;
    ...
    is = new FileInputStream("xml.ser");
    ois = new ObjectInputStream(is);
    
  2. 新規XML文書オブジェクトを作成して、解凍したデータを格納します。次のコード部分は、この方法を示します。

    XMLDocument serializedDoc = null;
    serializedDoc = new XMLDocument();
    
  3. XMLDocument.readExternal()をコールして、圧縮ファイルを読み取ります。次のコード部分は、データを読み取ってSystem.outに出力します。

    serializedDoc.readExternal(ois);
    serializedDoc.print(System.out);
    

SAXからXMLへの圧縮と解凍

SAXCompression.javaプログラムは、SAXを使用してファイルを解析し、圧縮ストリームをファイルに書き込み、シリアライズしたデータをファイルから読み取る基本手順を示します。重要なクラスは次のとおりです。

  • CXMLHandlerBaseは、SAXイベントに基づいてXMLデータを圧縮するSAX Handlerです。SAX圧縮を使用するには、このインタフェースを実装し、Parser.setDocumentHandler()をコールしてSAXパーサーに登録します。

  • CXMLParserは、圧縮ストリームからSAXイベントを再生成するXMLパーサーです。

SAXオブジェクトの圧縮

シリアライズの基本的な方法は、SAXパーサーにCXMLHandlerBaseハンドラを登録し、ObjectOutputStreamを初期化し、入力XMLを解析することです。SAXCompression.javaプログラムは次の手順を実行します。

  1. FileOutputStreamを作成し、ObjectOutputStreamにラップします。SAXCompression.javaの次のコード部分は、xml.serファイルを作成します。

    String compFile = "xml.ser";
    FileOutputStream outStream = new FileOutputStream(compFile);
    ObjectOutputStream out = new ObjectOutputStream(outStream);
    
  2. SAXイベント・ハンドラを作成します。CXMLHandlerBaseクラスは、ContentHandlerDTDHandlerEntityResolverおよびErrorHandlerインタフェースを実装します。次のコード部分は、この方法を示します。

    CXMLHandlerBase cxml = new CXMLHandlerBase(out);
    
  3. SAXパーサーを作成します。次のコード部分は、この方法を示します。

    SAXParser parser = new SAXParser();
    
  4. SAXパーサーを構成します。次のコード部分は、コンテンツ・ハンドラとエンティティ・リゾルバを設定し、検証モードも設定します。

    parser.setContentHandler(cxml);
    parser.setEntityResolver(cxml);
    parser.setValidationMode(XMLConstants.NONVALIDATING);
    

    oracle.xml.comp.CXMLHandlerBaseDocumentHandlerContentHandlerの両方のインタフェースを実装しますが、SAX 2.0 ContentHandlerインタフェースの使用が推奨されることに注意してください。

  5. XMLを解析します。プログラムは、シリアライズされたデータをObjectOutputStreamに書き込みます。次のコード部分は、この方法を示します。

    parser.parse(url);
    

SAXオブジェクトの解凍

SAXオブジェクトの非シリアライズの基本的な方法は、CXMLParserクラスを使用してSAX圧縮パーサーを作成し、パーサーのコンテンツ・ハンドラを設定し、圧縮ストリームを解析することです。

SAXDeCompression.javaプログラムは次の手順を実行します。

  1. SAXイベント・ハンドラを作成します。SampleSAXHandler.javaプログラムは、SAXDeCompression.javaで使用するハンドラを作成します。SAXDeCompression.javaの次のコード部分は、ハンドラ・オブジェクトを作成します。

    SampleSAXHandler xmlHandler = new SampleSAXHandler();
    
  2. CXMLParserクラスをインスタンス化することにより、SAXパーサーを作成します。このクラスは、圧縮ストリームからSAXイベントを生成することにより、その圧縮ストリームからのXML文書の再生成を実装します。次のコード部分は、この方法を示します。

    CXMLParser parser = new CXMLParser();
    
  3. SAXパーサーのイベント・ハンドラを設定します。次のコード部分は、この方法を示します。

    parser.setContentHandler(xmlHandler);
    
  4. 圧縮ストリームを解析し、SAXイベントを生成します。次のコードは、コマンドラインからファイル名を受け取り、XMLを解析します。

    parser.parse(args[0]);
    

XML解析のヒントと方法

この項の内容は次のとおりです。

DOMツリーからのノード値の抽出

XMLNodeクラスのselectNodes()メソッドを使用して、XSLが許可する選択パターンに基づいてDOMツリーまたはサブツリーからコンテンツを抽出できます。selectNodes()のオプションの2つ目のパラメータを使用して、名前空間の接頭辞を解決できます。つまり、接頭辞付きの展開された名前空間URLを戻します。XMLElementクラスはNSResolverを実装するため、XMLElementオブジェクトの参照は、2つ目のパラメータとして送信できます。XMLElementは、入力ドキュメントに基づいて接頭辞を解決します。名前空間の定義をオーバーライドする必要がある場合、NSResolverインタフェースを使用できます。

例4-4のサンプル・コードに、selectNodes()の使用方法を示します。

例4-4 selectNodes()を使用したDOMツリーのコンテンツの抽出

//
// selectNodesTest.java
//
import java.io.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class selectNodesTest
{
  public static void main(String[] args)
    throws Exception
  {
    // supply an xpath expression
    String pattern = "/family/member/text()";
    // accept a filename on the command line
    // run the program with $ORACLE_HOME/xdk/demo/java/parser/common/family.xml
    String file    = args[0];

    if (args.length == 2)
      pattern = args[1];

    DOMParser dp = new DOMParser();

    dp.parse(DemoUtil.createURL(file));  // include createURL from DemoUtil
    XMLDocument xd = dp.getDocument();
    XMLElement element = (XMLElement) xd.getDocumentElement();
    NodeList nl = element.selectNodes(pattern, element);
    for (int i = 0; i < nl.getLength(); i++)
    {
      System.out.println(nl.item(i).getNodeValue());
    } // end for
  } // end main
} // end selectNodesTest

プログラムをテストするには、例4-4のコードを使用してファイルを作成してから、$ORACLE_HOME/xdk/demo/java/parser/commonディレクトリ内でコンパイルします。ファイル名family.xmlをパラメータとしてプログラムに渡して、<family>ツリーを検索します。出力は次のようになります。

% java selectNodesTest family.xml
Sarah
Bob
Joanne
Jim

ここで、次のコードを実行して、文書内のすべての<member>要素のmemberid属性の値を判断します。

% java selectNodesTest family.xml //member/@memberid
m1
m2
m3
m4

appendChild()を使用した文書のマージ

ユーザーがクライアント側Javaフォームに入力でき、XML文書を取得できるように、プログラムを作成する必要があると想定します。Javaプログラムには、String型の次の変数が含まれているとします。

String firstname = "Gianfranco";
String lastname = "Pietraforte";

次のいずれかの方法を使用して、この情報をXML文書に挿入できます。

  • XML文書を文字列で作成し、解析します。次に例を示します。

    String xml = "<person><first>"+firstname+"</first>"+
         "<last>"+lastname+"</last></person>";
    DOMParser d = new DOMParser();
    d.parse(new StringReader(xml));
    Document xmldoc = d.getDocument();
    
  • DOM APIを使用して、要素を作成して順番に追加することによりXML文書を作成します。次に例を示します。

    Document xmldoc = new XMLDocument();
    Element e1 = xmldoc.createElement("person");
    xmldoc.appendChild(e1);
    Element e2 = xmldoc.createElement("firstname");
    e1.appendChild(e2);
    Text t = xmldoc.createText("Larry");
    e2.appendChild(t);
    

単一のDOMツリーでは2番目の方法のみ使用できることに注意してください。たとえば、例4-5に抜粋したコードを記述するとします。

例4-5 appendChild()の不適切な使用方法

XMLDocument xmldoc1 = new XMLDocument();
XMLElement e1 = xmldoc1.createElement("person");
XMLDocument xmldoc2 = new XMLDocument();
XMLElement e2 = xmldoc2.createElement("firstname");
e1.appendChild(e2);

前述のコードでは、e1の所有者文書がxmldoc1で、e2の所有者がxmldoc2であるため、XMLElement.appendChild()をコールしたときにWRONG_DOCUMENT_ERRのDOM例外が発生します。appendChild()メソッドは単一ツリー内でのみ動作しますが、例4-5では2つの異なるツリーを使用しています。

異なるXML文書間におけるDOM文書のフラグメントまたはDOMノードのコピーおよび貼付けには、DOM 2で導入されたXMLDocument.importNode()メソッドおよびDOM 3で導入されたXMLDocument.adoptNode()メソッドを使用できます。例4-6のコマンドラインは、このタスクの実行方法を示しています。

例4-6 appendChild()を使用した文書のマージ

XMLDocument doc1 = new XMLDocument();
XMLElement element1 = doc1.createElement("person");
XMLDocument doc2 = new XMLDocument();
XMLElement element2 = doc2.createElement("firstname");
// element2 = doc1.importNode(element2);
// element2 = doc1.adoptNode(element2);
element1.appendChild(element2);

DTDの解析

この項では、DTDの解析方法について説明します。この項の内容は次のとおりです。

外部DTDのロード

DOMParser.parse()メソッドをコールしてXML文書をInputStreamとして解析する場合は、DOMParser.setBaseURL()メソッドを使用してJavaプログラム内の外部DTDを認識します。このメソッドは、DTDが公開されている場所を指します。

DTDをロードして解析する方法を次の手順で説明します。

  1. DTDをInputStreamとしてロードします。たとえば、/mydir/my.dtd外部DTDに対して文書が妥当であるかどうかを検証する必要があるとします。次のコードを使用できます。

    InputStream is = MyClass.class.getResourceAsStream("/mydir/my.dtd");
    

    このコードによって、DTDが指定されているCLASSPATH上の最初の相対位置にある./mydir/my.dtdが開かれます。JARファイルがCLASSPATHにある場合は、そのJARファイルも開かれます。

  2. DOMパーサーを作成し、検証モードを設定します。たとえば、次のコードを使用します。

    DOMParser d = new DOMParser();
    d.setValidationMode(DTD_VALIDATION);
    
  3. DTDを解析します。次の例では、InputStreamオブジェクトをDOMParser.parseDTD()メソッドに渡します。

    d.parseDTD(is, "rootelementname");
    
  4. 文書タイプを取得し、設定します。getDoctype()メソッドはDTDオブジェクトを取得し、setDoctype()メソッドは解析に使用するDTDを設定します。次の例に、この方法を示します。

    d.setDoctype(d.getDoctype());
    

    次のコードは、別の方法を示しています。parseDTD()メソッドを起動して、DTDファイルを個別に解析し、DTDオブジェクトを取得できます。

    d.parseDTD(new FileReader("/mydir/my.dtd"));
    DTD dtd = d.getDoctype();
    parser.setDoctype(dtd);
    
  5. 入力XML文書を解析します。たとえば、次のコードはmydoc.xmlを解析します。

    d.parse("mydoc.xml");
    

setDoctypeによるDTDのキャッシュ

XML Parser for Javaには、DOMParser.setDoctype()メソッドを使用した検証モードと非検証モードでのDTDキャッシュ機能が用意されています。このメソッドでDTDを設定すると、パーサーはこのDTDをキャッシュして、さらに解析を実行します。DTDキャッシュ機能はオプションであり、自動的には有効にならないことに注意してください。

プログラムで、同じDTDを使用して複数のXML文書を解析する必要があると想定します。XML文書の解析後、次の例のようにパーサーからDTDを取得し、設定できます。

DOMParser parser = new DOMParser();
DTD dtd = parser.getDoctype();
parser.setDoctype(dtd);

パーサーはこのDTDをキャッシュして、後続のXML文書の解析に使用します。例4-7に、DOMParser.setDoctype()を起動してDTDをキャッシュする方法の詳細な例を示します。

例4-7 DTDSample.java

/**
 * DESCRIPTION
 * This program illustrates DTD caching.
 */

import java.net.URL;
import java.io.*;
import org.xml.sax.InputSource;
import oracle.xml.parser.v2.*;

public class DTDSample
{
   static public void main(String[] args)
   {
      try
      {
         if (args.length != 3)
         {
            System.err.println("Usage: java DTDSample dtd rootelement xmldoc");
            System.exit(1);
         }

         // Create a DOM parser
         DOMParser parser = new DOMParser();

         // Configure the parser
         parser.setErrorStream(System.out);
         parser.showWarnings(true);

        // Create a FileReader for the DTD file specified on the command
        // line and wrap it in an InputSource
        FileReader r = new FileReader(args[0]);
        InputSource inSource = new InputSource(r);

        // Create a URL from the command-line argument and use it to set the
        // system identifier
        inSource.setSystemId(DemoUtil.createURL(args[0]).toString());

        // Parse the external DTD from the input source. The second argument is
        // the name of the root element.
        parser.parseDTD(inSource, args[1]);
        DTD dtd = parser.getDoctype();

        // Create a FileReader object from the XML document specified on the
        // command line
        r = new FileReader(args[2]);

        // Wrap the FileReader in an InputSource, create a URL from the filename,
        // and set the system identifier
        inSource = new InputSource(r);
        inSource.setSystemId(DemoUtil.createURL(args[2]).toString());

        // ********************
        parser.setDoctype(dtd);
        // ********************

        parser.setValidationMode(DOMParser.DTD_VALIDATION);
       // parser.setAttribute(DOMParser.USE_DTD_ONLY_FOR_VALIDATION,Boolean.TRUE);
        parser.parse(inSource);

        // Obtain the DOM tree and print
        XMLDocument doc = parser.getDocument();
        doc.print(new PrintWriter(System.out));

      }
      catch (Exception e)
      {
         System.out.println(e.toString());
      }
   }
}

キャッシュされたDTDオブジェクトが検証に対してのみ使用される場合は、DOMParser.USE_DTD_ONLY_FOR_VALIDATION属性を設定します。DTDオブジェクトが検証に使用されない場合、XMLパーサーはDTDオブジェクトをコピーして、結果のDOMツリーに追加します。パーサーを次のように設定できます。

parser.setAttribute(DOMParser.USE_DTD_ONLY_FOR_VALIDATION,Boolean.TRUE);

XML Parserでのキャラクタ・セットの処理

この項の内容は次のとおりです。

オペレーティング・システム上でのXMLファイルの検出およびエンコーディング

オペレーティング・システム・ファイルに格納されたXMLファイルを読み取る場合は、FileReaderクラスを使用しないでください。かわりにXMLパーサーを使用して、文書の文字のエンコーディングが自動的に検出されるようにします。外部エンコーディング情報がないバイナリFileInputStreamの場合、パーサーは、バイト・オーダー・マークおよびXML文書のエンコーディング宣言に基づいて、文字のエンコーディングを自動的に判断します。AutoDetectEncoding.javaデモのサンプル・コードを使用して、サポートされている任意のエンコーディングでの任意の整形式文書を解析できます。このデモは、$ORACLE_HOME/xdk/demo/java/parser/domにあります。


注意:

仕様のとおりに、文書内で適切なエンコーディング宣言を含めます。setEncoding()では、入力文書のエンコーディングを設定できません。かわりに、出力に正しいエンコーディングを設定するためにoracle.xml.parser.v2.XMLDocumentで使用されます。

NCLOB列に格納されたXMLのエンコーディングの検出

UTF-8エンコーディングを使用して、XMLをデータベースのNCLOB列にロードするとします。XMLには、2つのUTF-8マルチバイト文字が含まれています。

G(0xc2,0x82)otingen, Br(0xc3,0xbc)ck_W

次の処理を行うJavaストアド・ファンクションを作成します。

  1. デフォルトの接続オブジェクトを使用して、データベースに接続します。

  2. SELECT問合せを実行します。

  3. oracle.jdbc.OracleResultSetオブジェクトを取得します。

  4. OracleResultSet.getCLOB()メソッドをコールします。

  5. CLOBオブジェクトに対してgetAsciiStream()メソッドをコールします。

  6. 次のコードを実行してXMLをDOMオブジェクトに変換します。

    DOMParser parser = new DOMParser();
    parser.setPreserveWhitespace(true);
    parser.parse(istr);
    // istr getAsciiStream XMLDocument xmldoc = parser.getDocument();
    

プログラムは、文字(0xc20x82)が有効なUTF-8であっても、XMLに無効なUTF-8エンコーディングが含まれているということを示す例外をスローします。問題は、プログラムがOracleResultSet.getAsciiStream()メソッドをコールするときに文字に異常が発生する可能性があることです。この問題を解決するには、getAsciiStream()のかわりに、getUnicodeStream()およびgetBinaryStream()メソッドを起動します。この方法が機能しない場合は、DOMParser.parse(istr)をコールする際に文字がパーサーに送信される前に、文字が異常でないことを確認するために出力してみてください。

デフォルト以外のエンコーディングでのXMLファイルの作成

FileWriterクラスは、実行時環境のデフォルトの文字エンコーディングに依存するため、XMLファイルを作成するときに、このクラスを使用しないでください。ドキュメントにデフォルトの文字エンコーディングで使用できない文字が含まれる場合、出力ファイルでは、解析エラーやデータの損失が発生する可能性があります。

UTF-8エンコーディングはXML文書には一般的ですが、UTF-8は、通常はJavaのデフォルトのファイル・エンコーディングではありません。デフォルトのファイル・エンコーディングを想定したJavaクラスをプログラムで使用すると、問題が発生する可能性があります。これらの問題を回避するために、$ORACLE_HOME/xdk/demo/java/parser/domI18nSafeXMLFileWritingSample.javaプログラムに示されている方法を使用できます。

特殊文字の出力にはSystem.out.println()を使用できないことに注意してください。OutputStreamWriterなど、エンコーディングを識別するバイナリ出力ストリームを使用する必要があります。次の例のように、OutputStreamWriterを構築し、write(char[], int, int)メソッドを使用して出力できます。

/* Java encoding string for ISO8859-1*/
OutputStreamWriter out = new OutputStreamWriter(System.out, "8859_1");
OutputStreamWriter.write(...);

文字列内のXMLの操作

Stringに含まれているXML文書を直接解析する方法は現在はありません。文字列は、解析する前にInputStreamまたはInputSourceオブジェクトに変換する必要があります。

1つの方法は、文字列内のバイトを使用するByteArrayInputStreamを生成することです。たとえば、xmlDocがXMLの文字列への参照であると想定します。例4-8に示す方法を使用して、文字列をバイト配列に変換し、配列をByteArrwayInputStreamに変換してから解析できます。

例4-8 文字列内のXMLの変換

// create parser
DOMParser parser=new DOMParser();
// create XML document in a string
String xmlDoc =
       "<?xml version='1.0'?>"+
       "<hello>"+
       "  <world/>"+
       "</hello>";
// convert string to bytes to stream
byte aByteArr [] = xmlDoc.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(aByteArr,0,aByteArr.length);
//  parse and obtain DOM tree
DOMParser.parse(bais);
XMLDocument doc = parser.getDocument();

前述のコードで作成したXMLDocumentオブジェクトを再び文字列に変換するとします。このタスクは、PrintWriterStringWriterをラップすることで実行できます。次の例に、この方法を示します。

StringWriter sw = new StringWriter();
PrintWriter  pw = new PrintWriter(sw);
doc.print(pw);
String YourDocInString = sw.toString();

$ORACLE_HOME/xdk/demo/java/parser/domにあるParseXMLFromString.javaは、XML文書を文字列として作成し、それを解析する完全なプログラムです。

アクセント付き文字を含むXML文書の解析

入力XMLファイルに、éなどのアクセント付き文字が含まれているとします。例4-9に、アクセント付き文字を含むXML文書の解析方法の1つを示します。

例4-9 アクセント付き文字を含む文書の解析

DOMParser parser=new DOMParser();
parser.setPreserveWhitespace(true);
parser.setErrorStream(System.err);
parser.setValidationMode(false);
parser.showWarnings(true);
parser.parse (new FileInputStream(new File("file_with_accents.xml")));

XMLファイルを解析する場合、パーサーは、「UTF8エンコーディングが無効です。」例外をスローすることがあります。エンコーディングを明示的にUTF-8に設定した場合、またはエンコーディングを指定しない場合、パーサーはアクセント付き文字(127より大きいASCII値の文字)をUTF-8マルチバイト文字列の最初のバイトとして解析します。後続のバイトが有効なUTF-8文字列を構成していない場合、エラーが発生します。

このエラーは、XMLエディタがファイルをUTF-8エンコーディングを使用して保存していないことを意味します。たとえば、エディタはISO 8859-1エンコーディングを使用してファイルを保存している場合があります。エンコーディングはUnicodeの文字番号表現をディスクに書き込むために使用される特定のスキーマです。次の要素をXML文書の最上部に追加しても、エディタはファイルを表すバイトをUTF-8エンコーディングを使用してディスクに書き出しません。

<?xml version="1.0" encoding="UTF-8"?>

1つの解決策として、アクセント付き文字は、XML文書内で&#xd9;のように16進または10進フォーマットで表すと読み取ることができます。ただし、この方法を使用しない場合は、XMLファイルの作成時に使用したキャラクタ・セットに基づいてエンコーディングを設定できます。たとえば、使用しているツールまたはオペレーティング・システムに応じて、エンコーディングをISO-8859-1(Western European ASCII)またはそれ以外に設定してみてください。

タグ名内の特殊文字の処理

&$#などの特殊文字はタグ名として有効ではありません。たとえば、文書が会社名に基づいた名前をタグに付けていて、文書にタグ<A&B>が含まれる場合、パーサーは無効な文字に関するエラーを発行します。

XML文書を最初から作成する場合は、有効なNameCharsのみを使用すると、この問題を回避できます。たとえば、タグに<A_B>、<AB><A_AND_B>などの名前を付けることができます。ただし、データベース表などの外部データ・ソースからXMLを生成する場合、XML 1.0はこの問題に対応していません。

データ型XMLTypeは、DBMS_XMLGENパッケージでsetConvertSpecialCharsおよびconvertファンクションを提供することで、この問題に対処しています。これらのファンクションを使用して、SQL名とXML名の特殊文字の使用を制御できます。SQLからXMLに名前をマップする機能によって、_XHHHH_という形式(HHHHは無効な文字のUnicode値)の無効なXML NameChar文字がエスケープされます。たとえば、表名V$SESSIONはXMLの名前V_X0024_SESSIONにマップされます。

無効な文字のエスケープも、名前を他の場所に再ロードできるようにシリアライズする方法をユーザーに提供する有効な手段です。