Xerces-C++を最大限に利用する-パート1

C++プログラマのための構文解析の手引

この2部構成の記事は、Xerces-C++ XMLライブラリについて入門者向けに書かれています。パート1ではLinux及びWindows上で書かれたアプリケーションにライブラリをリンクする方法について述べます。豊富なコードで、SAX APIによる構文解析を例証します。またサンプル・アプリケーションでは、ASCIIアートで棒グラフを作成する方法を説明します。パート2では、DOMドキュメントのロード、操作、合成の方法を取り上げます。また、同じ棒グラフをScalable Vector Graphics (SVG)を使って作成する方法について述べます。この記事を読んだC++プログラマは、独自のアプリケーション機能にXMLの構文解析、プロセッシングを簡単に追加できます。

Rick Parrish (rfmobile@swbell.net), Independent consultant

Rick Parrish氏は、高校生の頃からコンピューター・サイエンスに興味を持ち始めましたが、エレクトロニクスにはもっと早くから関心を持っていました。最初は電気工学の教育を受けていましたが、ソフトウェアの場合は、ハードウェアとは違って、塩化第二鉄溶液の嫌な臭いをかぐ必要はなく、ただ設計変更のためだけに指にやけどをする危険を冒す必要もないことに気付きました。Rickは、長い間C/C++ のプログラミングに携わってきましたが、VBとDelphi(Pascal) による仕事も多く、アセンブリー言語の仕事も少なくありません。今でも、熱いハンダを必要とする1つか2つのプロジェクトに何とか携わっています。Windows 2000は素晴らしいけれど、Ogg Vorbisもクールだし、IPTableのあるLinux 2.4カーネルは本当にタフだ! というのが彼の現在の持論です。現在の関心事は、ソフトウェア・モデリング設計用のツールを開発するオープン・ソース・プロジェクトを始めることです。Rickの連絡先は、 rfmobile@swbell.net です。



2003年 8月 13日

Xerces-C++は、SAXとDOMのAPIに加え、妥当性検証を実施できる非常に堅牢なXMLパーサです。XMLの妥当性検証に関しては、Document Type Definition (DTD) を完全にサポートしており、基本部分が完成したオープン・スタンダードのW3C XML Schemaに対するサポートも2001年12月に追加されました。

Xerces-C++: 略歴

Xerces-C++はIBMでXML4Cプロジェクトとして始まりました。XML4CはXML4Jと対のプロジェクトであり、Xerces-J (Javaでの実装)の原点でもありました。IBMはApache Software Foundationに両プロジェクトのソースをリリースし、それぞれXerces-C++およびXerces-Jと名前が変わりました。これら2つのプロジェクトは、Apache XMLグループの中核となるものです。(「Xerces-C」を目にしたとしても、プロジェクトは最初からC++で書かれているので「Xerces-C++」と同じものです。)

XML4Cプロジェクトは、Xerces-C++をベースにしてIBMで続けられています。調査したバージョンのXML4Cの卓越した長所は、Xerces-C++と比較して膨大な各国語の文字エンコードを独創的にサポートしていることです(参考文献を参照してください)。

妥当性の検証

XML文書の構造を指定する基本的な2つの手段はDTDおよびW3C XML Schemaですが、DTD自体はこの2つよりも前から存在しています。XML Schemaは、おおむねXMLで表現されたDTDと言っても良いでしょう。Xerces-C++には、XML文書がDTDに準拠するか否かを保証する独自の妥当性検証機能があります。

ライセンス

Xerces-C++は、Apache Software License(参考文献を参照)の条件下で使用可能であり、いろいろなところで散見されるオープン・ソース・ライセンスのうちの1つです。それはBSDライセンスと非常によく比較されます。基本的にXerces-C++では、自分の(あるいは会社の)ソフトウェアにApacheのコードが含まれていることを顧客やユーザに明示し、適切な著作権表示をするわずかな費用だけで、ロイヤリティーなしに使用できます。ライセンスに関する正確な条項についてはWebページで確認してください。


SAX: イベントAPIモデル

ご存知かもしれませんが、SAXはXML文書の構文解析をするためのイベント指向のプログラミングAPIです。解析エンジンはXMLデータをシーケンシャルに処理し、読み込まれたXMLデータに特定の構文を見つけた時、アプリケーションにコールバックします。これらのコールバックは、イベント・ハンドラで参照されます。SAXには2つのAPIが存在します。SAX 1.0がオリジナルで、SAX 2.0が現在の改訂仕様です。2つは類似していますが、新仕様に移行した場合、SAX 1.0に基づいたほとんどのアプリケーションが動作しなくなるほどには十分に異なっています。

SAX APIの仕様は独自のプロジェクトとしてSourceForgeに移されました(参考文献を参照してください)。この記事の後で述べるSAXの例は、SAX2.0を使用して作成しています。


DOM: ドキュメント・オブジェクト・モデル

SAXと異なり、DOM APIはXML文書を編集しファイルやストリームに保存できます。またプログラムでゼロから新しいXML文書を作成できます。これはDOMが文書をメモリ内に持つ設計になっているためです。DOMでは文書木のトラバース、ノードの削除や新しいノードの追加をすることができます。

テク・レック

DOMはW3C技術勧告の一員であり、親しみを込めてテク・レック (tech recs ではなく tech wrecks)と呼ばれます。DOMには、3つのレベルがあります。レベル1とレベル2は技術的に完成され勧告となっていますが、レベル3のステータスはワーキング・ドラフトのままです。

DOMレベル1のコアは、基本的なXMLの機能(XML文書の表現を構成する機能)に必要な大部分を定義しています。DOMString型は、明確にUTF-16文字で構成すると規定されています。続けてレベル1では、プログラムから様々なDOMツリーを扱うためのインターフェースが定義されています。XMLのシリアライズ(永続化)に関しては、レベル1では意図的に省略されています。レベル1のコア以降では、DOMレベル1 HTMLが定義されています。こちらの部分では、初期のダイナミックHTMLオブジェクト・モデル(レベル0 とか DOM-0 などと呼ばれていました)を、DOMレベル1のコアで解決することを試みます。

DOMレベル2では、名前空間、イベント、イテレータ、および、ビュー、スタイルシートのサポートを追加します。幾つかのアプリケーションでは、DOMレベル2を必要としています。例えば、XML Schemaに名前空間を割り当てることは、RDFのようなアプリケーションにとって不可欠です。そこではXMLのタグは異なるスキーマによってもたらされるため、名前衝突の機会が高くなります。レベル2では、DOMImplementationインターフェースに2つのcreateDocumentメソッドを追加します。例のうちの1つでなぜこれが重要なのか分かるでしょう。SAXでのコールバックとイベント・ハンドラが心配無くなったと思っていたのに、それらは再びイベント・インターフェースとして存在します。構文解析用のSAXのイベントとは異なり、DOMのイベントは実際の文書への変更や、文書に関連したユーザ操作によって発生します。文書構造の変更によって生じるDOMのイベントは、ミューテイション・イベントと呼ばれます。TreeWalkersNodeIteratorsは、DOMツリーのトラバーサルを向上させます。プログラムからStyleSheetインターフェースを使ってスタイル情報を調べることができます。最後に、ビューのサポートにより、XMLアプリケーションがオリジナルおよびスタイルシートでレンダリングされた両方の形式で文書を調べることが可能になりました。これらはビューの前と後でそれぞれ、DocumentViewAbstractViewと呼ばれます。

DOMレベル3のコアはDOMImplementationインターフェースにgetInterfaceメソッドを追加します。レベル3の文書では、文書の文字エンコードを指定したり、versionstandaloneのような基本的なXML宣言のうちのいくつかをセットしたりできます。レベル2では、ある文書から別の文書にDOMノードを移動できません。レベル3ではこの制限が外れています。レベル3ではユーザ・データ (任意のノードにオプションとして付加できるアプリケーションデータ)を追加します。レベル3には他にも幾つかの拡張機能がありますが、W3C委員会ではまだレベル3はワーキング・ドラフトとして止まっています。委員会での進捗状況に関しては参考文献を確認してください。


ダウンロードとインストール

圧縮されたTARファイルあるいはプリコンパイルされたバイナリとしてXercesC++をダウンロードすることができます(参考文献を参照)。Perl、Python、VBScriptあるいはJavaScriptによってライブラリにアクセスするスクリプト・ユーザは、それぞれのプラットフォームで手軽にインストールするためのバイナリをダウンロードできます。たいていのC++プログラマは、TARファイルのソースから独自にバイナリを作ることを好むでしょう。Apache XMLグループのWebサイトにはビルドに関する説明が丁寧に書かれています。この記事を書き進める前に、私が発見した2、3の微妙な問題(pthreadsのリンクに関する問題と、Windowsプラットフォームにおける潜在的なメモリ・リークの問題)について取り上げます。パート2では、SVGの例においてDOCTYPEを指定するためのヒントを書こうと思います。この記事を読み進めるに従ってライブラリをビルドしたくなったら、最初にApacheサイト(参考文献を参照)にあるXercesのビルドに関する説明を読んで、次にXercesをあなたのアプリケーションにリンクするヒントを読むためにここに戻ってください。

TARファイルをダウンロードして、オフラインで(例えばノートPCで)作業することができます。完全なHTML文書がTARファイルに含まれていますので、説明を読むためにWebサイトを参照し続ける必要はありません。

Win32でのビルド

Visual Studio .NETあるいは、Win64にソフトウェアをインストールするためのステップは、Win32用にビルドするためのこれらのステップとほとんど同じです。

  1. 作業ディレクトリにXercesソースのTARファイルを解凍してください。Xerces-C++は独自のディレクトリ構造を持っていますので、このステップの間、相対パス名が保存されることを確認しなければなりません。
  2. Windowsエクスプローラもしくは好みのファイル管理プログラムを使用して、\xerces-c-src_2_3_0\Projects\Win32\VC6\xerces-all\フォルダまでたどり、Microsoft Developer Studioを起動するためにxerces-all.dswワークスペース・ファイルをクリックします。
    注: これらの指示は、あなたがVisual Studio 6でWin32アプリケーションを作成していると仮定しています。Visual Studio .NETかWin64アプリケーションについては、ディレクトリをWin64またはVC7に読み替えて、ステップ1および2を繰り返してください。
  3. Developer Studioから、XercesLibを現在のアクティブなプロジェクトにして、DLLを作成するためにF7を押してください。最近のハードウェアならば、1、2分ですみます。
  4. あなたのプロジェクトにXercesヘッダ・ファイルのパスを追加してください。(XercesC++をリンクする必要のあるアプリケーションでは、それらのワークスペースにXercesLib DSPプロジェクト・ファイルを含めるか、あるいはリンクを可能にするためにプロジェクト・ファイルに、LIBファイルを追加する必要があります)。「プロジェクトの設定」ダイアログボックスを表示するために[プロジェクト(P)]-[設定(S)…]メニューを選択してください。「設定の対象」コンボ・ボックスから「全ての構成」を選んでから、「C/C++」タブをクリックし、「カテゴリ」から「プリプロセッサ」を選択して、「インクルードファイルのパス」テキストボックスにXercesのインクルードのパス (例えば、\xerces-c-sr2_2_0\srcのように)を追加します。
  5. ワークスペースにXercesLib DSPを追加した場合は、あなたのプロジェクトの依存関係で、XercesLibプロジェクトに依存することを忘れずにマークしてください。そうでなければ、リンク・エラーとなります。
  6. #include <xercesc/sax/HandlerBase.hpp>を読み込むだけの1行のスタブC++ソース・ファイルを作成してください。この1行のC++ファイルがコンパイルできれば、おそらくインクルード・パスは適切です。その後、ワークスペースを保存してください。あなたのアプリケーションを実行あるいはデバッグするためには、作業ディレクトリにXerces DLLのコピーを置いてください。

Linuxでのビルド

doc/htmlフォルダ中の完全な説明に従ってXerces-C++共有ライブラリを作成してください。下記のコマンドは、圧縮されたソースからXercesC++ライブラリを作成する方法を示しています。この例では、xerces-c-src_2_3_0.tar.gzファイルが/home/userなどのディレクトリ中にあると仮定しています。選択したインストールディレクトリをXERCESCROOT変数に設定します(configureスクリプトでその設定が必要です)。

# cd /home/user
# gunzip xerces-c-src_2_3_0.tar.gz
# tar -xvf xerces-c-src_2_3_0.tar
# export XERCESCROOT=/home/user/xerces-c-src_2_3_0
# cd $(XERCESCROOT)/src/xercesc
# ./configure
# make all

この例の残りの部分において、ソース・ツリーは/home/user/xerces-c-src_2_3_0 ディレクトリ以下にあると想定しています。すべてがうまくいけば、共有ライブラリがlibフォルダに作成されます。問題が発生した場合には、/doc/htmlフォルダにあるビルドの説明を良く読んでください。それから、/usr/libにライブラリ(そのシンボリック・リンク)をコピーするか、あるいはローダーが新しくコンパイルされたライブラリを見つられるように、適切な環境変数を定義します。

新しいライブラリを実際にテストする簡単な方法は、以下のようにサンプルのうちの1つをビルドして実行してみることです。

# export XERCESCROOT=/home/user/xerces-c-src_2_3_0
# cd $(XERCESCROOT)/samples
# ./configure
# make all

今回はSlackware Linux 9.0を新たにインストールしてサンプルのうちの1つを作成したのですが、小さな問題でつまずきました。pthreads関連のエクスポートがいくつか見当たらないため、リンクの段階でエラーが起きたのです。私は、Makefile.inファイルを編集して-lpthread の参照を追加し、再びconfigure を実行しました。今度はmake all が、きちんと動作しました。

一旦ライブラリが動作することが分かれば、あなた自身のXercesC++プロジェクトを開始できます。コンパイラがXercesのヘッダ・ファイルを見つけられるように-I コンパイラ・オプションを使用してください。リンカがXercesC++ライブラリを見つけられるように-L および-l リンカ・オプションを使用してください。すぐに試せるように、実用的で最小のmakefileをリスト1に示します。

リスト1: 最小のmakefile
APP = example
XERCES = /home/user/xerces-c-src_2_3_0
INCS = ${XERCES}/src
${APP} :: ${APP}.cpp
	${CC} -lxerces-c-src_2_3_0 -I${INCS} ${APP}.cpp -o ${APP}

リスト1を使うためのコマンドはmakeまたはgmakeです。APP変数を変更すれば、どんなソース・ファイルにでも適応することができます。本記事の例では、よく似たmakefilesを使用します。

Xerces C++はバージョン2.2.0で、C++の名前空間のサポート(XMLの名前空間と混同しないでください)を追加しました。2.1.0で動作するコードを持っており、より新しいバージョンを利用したい場合は、そのコードのXerces C++ヘッダをインクルードした直後に、次の3行を追加します。

リスト2: Xerces C++名前空間のサポート
#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif

もちろん、あなたの全てのXerces-C++オブジェクトにXERCES_CPP_NAMESPACE:: 名前空間をプレフィックスすることができます。


サンプル・アプリケーション

Xerces-C++の使用法の基本を説明するにあたって、興味を持ってもらうために、データフォーマットにXMLを使用した単純な棒グラフを作成しようと思います。特定のプラットフォームのGUIによらず、さまざまな環境で動作するように、ASCIIアートを使用して棒グラフを作成します。その理由は、この記事がGTK、OpenGLやDirect-Xについてではなく、XMLを取り上げているからです。XMLによるグラフィックデータの表現方法に興味がある場合は、SVGやSMILを調べてください(参考文献を参照してください)。パート2では、SVGを出力するDOMの例を紹介します。まずは、単純なテキストのみのアプリケーションから始めましょう。

リスト3はデータ用のDTDです。次に、データをロードし、どれぐらいのスケールを使用するのかを決め、実際にスクリーンにデータをプロットするプログラムを作成します。

リスト3: サンプル・アプリケーション・データ用のDTD
APP = example
<?xml version="1.0" ?>
<!ELEMENT figures (PCDATA) >
<!ATTLIST figures type (sales | inventory | labor) >
<!ATTLIST figures value CDATA >
<!ELEMENT department (figures*) >
<!ATTLIST department name CDATA> 
<!ELEMENT corporate (department*) >
<!ATTLIST corporate name CDATA >

リスト4に、データがどんな形になるかのサンプルを示します。

リスト4: 入力XMLデータのサンプル
APP = example
<?xml version="1.0" ?>
<corporate name="Big Biz">
<department name="North">
<figures type="sales" value="125000.00"/>
<figures type="inventory" value="90000.00"/>
<figures type="labor" value="110000.00">estimated</figures>
</department>
<department name="South">
<figures type="sales" value="980000.00"/>
<figures type="inventory" value="110000.00"/>
<figures type="labor" value="115000.00">estimated</figures>
</department>
<department name="East">
<figures type="sales" value="210000.00"/>
<figures type="inventory" value="80000.00"/>
<figures type="labor" value="95000.00">estimated</figures>
</department>
<department name="West">
<figures type="sales" value="160000.00"/>
<figures type="inventory" value="75000.00"/>
<figures type="labor" value="130000.00">estimated</figures>
</department>
<department name="Central">
<figures type="sales" value="723000.00"/>
<figures type="inventory" value="11000.00"/>
<figures type="labor" value="221000.00">estimated</figures>
</department>
</corporate>

SAX2の実装

リスト5は基本的なSAXの実装です。ハンドラの実装が見当たらないので、これは完全なプログラムではありませんが、必要な機能をどこにおくべきかを正確に示しています。XMLPlatformUtils:Initialize()XMLPlatformUtils::Terminate() の呼出しは、非常に重要です。このライブラリは、例外をスローすることにより、ライブラリを適切に初期化できないアプリケーションをガードします。

リスト5で作ったプログラムを完全なアプリケーションにするためには、リスト6のイベント・ハンドラのクラスを追加する必要があります。SAX2は、DefaultHandlerと呼ばれるディフォルトのイベント・ハンドラ・クラスを持っています(同じ名前のヘッダ・ファイルで定義されています)。ディフォルトのハンドラは何もしません(それは単にスタブ実装です)が、ハンドラとして完成しています。したがってここでは、描画用のイベント・ハンドラ・クラスの基本クラスとして使用しています。

リスト7に示したファイルは、実際に動作するリスト6のイベント・ハンドラ・クラスの実装です。これ以外のプログラムの大部分は、SAX2パーサを実行させるためのテンプレート的なコードにすぎませんが、対してこのリスト7の部分では、アプリケーションの固有機能を定義しています。

Xerces-C++では、文字を表現する型宣言としてXMLChを使用します(実際のtypedefは、処理系によって異なります)。多くのプラットフォームでは、Cのwchar_t型と互換性を持っており、通常2バイト長です(4バイトの場合もあります)。バイト長が異なる可能性があるため、Xerces-C++のドキュメントではwchar_tXMLChとの混用を認めていません。いくつかのプラットフォーム上では、それでもその場を切り抜けることができますが、他のプラットフォーム上にも当てはまるとは限りません。Xerces-C++では、UTF-8あるいはISO-8859ではなく、UTF-16でテキスト交換を行うために、この(より大きな)文字表現を使用しています。このプログラムのデバッグにあたり、図1のような画面表示をするためにワイド文字列を変換するXMLString::transcode関数を使用しています。

図1: SAXパーサの出力例
図1: SAXパーサの出力例

今回、Microsoft Windows上でXerces内部のストリング・クラスを使用した時に問題を発見しました。XMLString.hppのコメントで、replicateおよび類似の機能において、呼出し側で戻り値のメモリを解放する必要があると要求しています。この問題は、あなたのアプリケーションにDLLとしてXerces-C++ライブラリをリンクすることに起因しています。文字列はDLLのローカル・ヒープから割り当てられます。あなたのアプリケーションとXercesLib DLLの両方が、全く同じCランタイム(CRT)ライブラリDLLを使用する場合は何の問題もありません。しかしながら、あなたのプログラムがシングル・スレッドのCRTを使用し、XercesLibがマルチ・スレッドのCRTを使用している場合にはDLLに問題が起こります。プログラムで文字列のメモリを解放しようとした場合、Cランタイムはメモリがあなたのアプリケーションのローカル・ヒープで確保されたのではないことに気づきます。デバッグ・ビルドであれば、例外が発生しますが、リリース・ビルドでは、暗黙のうちにメモリ・リークしているかもしれません。Xercesの旧バージョン(1_5_1のような)にあるサンプル・プログラムでは、単にメモリを解放しないことによりこれを回避していました。

この問題は、XMLStringクラスに1対の解放用の静的関数を追加することで解決できます。文字列のメモリはDLL内部で実行するコードによって解放される(適切なローカル・ヒープが使用される)ため、デバッグのアサーションは発生しません。Xercesの開発者Tinny NgがXMLStringクラスにこの機能を追加し、文字列ポインタが空かどうかにまで踏み込んでいることが分かり嬉しく思いました(参考文献を参照してください)。文字列メモリに関する他の優れた特徴は、XMLStringの実装がどのようにメモリを確保しているかをプログラマが気にする必要がないということです。メモリを解放するときに、delete[] を使うべきかfree を使用すべきかどうか悩む代わりに、XMLString::release を呼び出せばいいのです。もちろん、あなたのアプリケーションが期待するCRTがXercesLib DLLによって使用されているCRTと同じであるか確認することもできます。


次回の記事では

パート1では、LinuxとWindows上で書かれたアプリケーションにXerces-C++ XMLライブラリをリンクする方法を見てきました。また、ASCIIアートで棒グラフを作成することによりSAX APIでの構文解析を実証しました。パート2では、DOMドキュメントのロード、操作あるいは合成、そしてScalable Vector Graphics (SVG)を使用して、同じ棒グラフを作成する方法を紹介します。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=243020
ArticleTitle=Xerces-C++を最大限に利用する-パート1
publish-date=08132003