Xerces/Xalan-C++ で XML

SAXで単純なプログラミング

最終更新: 2015-03-31 (火) 22:05:09 (1359d)

概要

目的

XML パーサを使った最も単純なプログラムの1つを紹介する。 上記プログラムを通して下記の説明を行う。

  • XML を使うためには、XML パーサを利用する。
  • XML パーサを使って XML ドキュメントから得られた情報を受け取る方法には SAX と DOM がある。
  • SAX を使ったプログラムを例示する。

このページで説明するコード

注意

unsigned int 型となっている箇所を XMLSize_t に変更した。

これは Xerces-C++ 3.0.0 以降のリリースに影響しての変更である。

Xerces-C++ 2 系の利用者は、XMLSize_t を unsigned int と読み替えること。

XMLSize_t 型について調査した結果は以下のブログ記事にて言及している

fileフルセット(VC++ 6.0/Xerces-C++ 2 系用)では unsigned int を用いているが、それ以外のファイルは XMLSize_t を利用している。

XML を扱うための道具を整備する

Xerces-C++ とは、XML をコンピュータに読ませたり、 読ませたものを再び書き出したりという処理を行ってくれるライブラリだ。 XML を読み込んで解析してくれるソフトウェアのことを XML パーサという。 すなわち、Xerces-C++ は XML パーサを提供してくれる。 名前の後ろに付いている通り、C/C++ で使うことができる。

読み込んだ情報を実際に使うのは僕らなわけで、 XML パーサが読み込んだ情報を僕らは受け取って使わなければならない。 情報を受け取るためのインターフェイスには、 SAX(Simple API for XML)DOM(Document Object Model) の二つがあり、 それによってプログラミングのスタイルが変わる。 どちらの方式をとるにしても、このライブラリが必要だ。

上のリンクをたどると Xerces-C++ のページに行ける。 英語で書かれたページだけど、ダウンロード&セットアップをがんばって済ませよう。 ダウンロードは自分のプラットフォーム(OS)を間違えないこと。 セットアップでするべきことを以下に簡単に挙げる。

  • インクルードパスを設定する
    • xerces-c のフォルダ/include
  • ライブラリパスを設定する
    • xerces-c のフォルダ/lib
  • 実行パスを通すか、または DLL を実行パスの通ったディレクトリへコピーする
    • xerces-c のフォルダ/bin

SAX とは?

SAX とは、XML から得られた情報を受け取る方法の一つだ。 SAX では、XML パーサが XML ドキュメントを順に読んで行き、 何らかの要素を発見したときにそれをどのように処理するかを決める 対話型のプログラミングモデルである。

SAX

例えばタグが発見されたときにそのタグ名を出力するなどの処理を実現できる。 SAX では要素が発見されたとき、どのメソッドが呼ばれるのかがあらかじめ定義されており、 そのメソッドを実装することによってプログラミングを行う。

要素が発見されたときに呼ばれるメソッドは、 DefaultHandler というクラスにまとめて定義されている。 例えば開始タグが発見されたときに呼ばれるメソッドとして startElement メソッドが、 終了タグが発見されたときに呼ばれるメソッドとして endElement メソッドが定義されている。 SAX を用いたプログラミングでは、 DefaultHandler クラスを継承した自前のクラスを定義することが中心となる。

タグと、テキストを出力するプログラムを作る

作るもの

これから fileexample.xml という XML ファイルを読み込み、 以下の処理を行う簡単なプログラムを製作する。

  • 開始・終了タグの名前を出力する。
  • タグ内のテキストを出力する。

大まかな流れとしては、 まず最初に、XML パーサから情報を受け取り処理するクラスとして SampleHandler クラスを製作する。 次に、main 関数で fileexample.xml を実際に読み込むようにする。

SampleHandler クラスを作る

SAX では、XML パーサから情報を受け取り、それを処理するためのクラスを一つ用意する。 今回は SampleHandler クラスを用意した。 SampleHandler クラスの定義として、fileSampleHandler.hpp の一部を以下に示す。

#include <xercesc/sax2/DefaultHandler.hpp>

class SampleHandler : public xercesc::DefaultHandler {
public:
  void startElement(const XMLCh* const uri, const XMLCh* const localname,
                    const XMLCh* const qname, const xercesc::Attributes& attrs);
  void endElement  (const XMLCh* const uri, const XMLCh* const localname,
                    const XMLCh* const qname);

  void characters(const XMLCh* const chars, const unsigned int length);
};

SampleHandler クラスには、DefaultHandler クラスを継承させる。 また、XML パーサが開始タグを検出したときに呼び出される startElement メソッドと、 終了タグを検出したときに呼び出される endElement メソッドと、 タグ内のテキストを検出したときに呼び出される characters メソッドを定義する。 これらのメソッドはあらかじめ DefaultHandler クラスで定義されているもので、 何が起こったときに何のメソッドが呼ばれるのかを知りたいときは、 DefaultHandler クラスのメソッドを調べればいい。 (調べるためには このページを参照すると良い。 同じページが Xerces-C++ に付属のドキュメントにも存在する。)

次に SampleHandler クラスの具体的なメソッドの中身を見てみよう。 startElement メソッドの定義として、fileSampleHandler.cpp の一部を以下に示す。

void SampleHandler::startElement(const XMLCh* const uri, const XMLCh* const localname,
                                 const XMLCh* const qname, const Attributes& attrs) {
  char* name=XMLString::transcode(localname);
  cout << "start  : " << name << endl;
  XMLString::release(&name);
}

startElement メソッドはいくつかの引数を受け取るが、 今回必要な情報はタグの名前である。localname 引数が丁度それに当たる。 なので localname を出力したいが、XMLCh という型ってなんだろう? これは Xerces-C++ が char 型に代わって内部で文字列を扱っているデータ型であり、 このままでは画面に出力することはできない。 そこで XMLString クラスの transcode メソッドを使う。 これを使うと XMLCh 型のデータを char 型のデータに変換できる。 transcode メソッドで生成した char 型のデータは使い終わったら release で開放するのを忘れないようにしよう。

main 関数を作る

さて SampleHandler クラスを作り終えたら、 main 関数で実際に XML パーサに XML ファイルを読み込ませるプログラムを書こう。 filemain.cpp の一部を以下に示す。

int main() {
  // Xerces-C++を初期化する
  try {
    XMLPlatformUtils::Initialize();
  } catch(...) {
    cerr << "Xerces-C++の初期化に失敗しました。" << endl;
    return 1;
  }

  SAX2XMLReader* parser=XMLReaderFactory::createXMLReader();

  try {
    SampleHandler handler;
    parser->setContentHandler(&handler);
    parser->parse("example.xml");
  } catch(...) {
    cerr << "ファイルの解析に失敗しました。" << endl;
  }

  delete parser;

  XMLPlatformUtils::Terminate();

  return 0;
}

まず Xerces-C++ は、使う前に XMLPlatformUtils::Initialize で初期化しなければならない。 さらに使い終わったら XMLPlatformUtils::Terminate で終了しなければならないことを覚えよう。

Xerces-C++ における SAX のための XML パーサは、SAX2XMLReader クラスである。 それを生成したら setContentHandler メソッドで、 さっき作った SampleHandler に情報を通知してくれるように依頼する。 最後に parse メソッドで fileexample.xml を読み込ませればいい。

日本語への対応とより簡潔な記述

本ページで紹介した SampleHandler::writeElement メソッドは、XML ドキュメントに日本語があると正常に動作しない。

以下のページで日本語を扱う仕組みについて説明している。

上記のページを参考にしても良いし、 本コンテンツ内で技術的解説を加えた上で公開しているクラスをオープンソースのユーティリティーとして sourceforge.jp で公開しているのでそちらを利用することもできる。

Xerces-C++ Utils(sourceforge.jp) を利用すると以下のように簡潔に記述できる。

void SampleHandler::startElement(const XMLCh* const uri, const XMLCh* const localname,
                                 const XMLCh* const qname, const Attributes& attrs) {
  xercesc_utils::XMLCh2CharTranscoder toTranscoder("Shift_JIS");
  cout << "tag    : " << toTranscoder::transcode(localname) << endl;
}

コメント

最新の10件を表示しています。 コメントページを参照

  • DefaultHandler クラスのリンクが切れているので、後で修正します。 -- トゥイー 2011-06-10 (金) 09:49:12
  • リンク切れ直しました。割れた窓は見つけたら直さないとね。 -- トゥイー 2011-07-11 (月) 21:43:40
  • startElement()のattrsを出力しようとしたら#2070のコンパイルエラーで小一時間ほどハマリました。Attributes.hppのincludeがサンプルには足りないのかと思います。 -- 名無し 2013-03-26 (火) 09:36:47
  • Xerces-C++ は1つのファイルをインクルードすれば機能全部が使えるというものではなく、利用する機能に応じてインクルード文を追加する必要があります。これは Xerces-C++ の機能提供方針と思われます。Attributes 型にアクセスする場合は、#include <xercesc/sax2/Attributes.hpp> する必要があります。サンプルは必要最小限に、不要なものは書かない方針のため、このサンプルでは Attributes 型にアクセスしていないので特にインクルードしませんでした。サンプル上は問題ありません。Attributes だけではなく、他にも新しい機能を使う場合は適宜インクルードファイルを追加する必要があります。 -- トゥイー 2013-03-30 (土) 10:43:37
  • どのクラスを使うときどのファイルをインクルードする必要があるのかという対応は、公式のドキュメントを探してありません。ここは C++(より正確には Xerces-C++)の特性ですので、ある程度感が必要になります。 -- トゥイー 2013-03-30 (土) 10:47:14
  • ちなみにサンプル上になぜ Attributes の記述があるのに、使っていないときにはコンパイルできて、使おうとするとコンパイルできなくなるのか?そのポイントは引き数の Attributes が実体ではなく参照だからです(もしくはポインターでも同じような特性があります)。なぜこういうようなことをするのかには意味があって C++ の基礎/定石であったりするので、勉強してみると良いと思います。Effective C++More Effective C++ 辺りを読んで勉強すると良いと思います。大規模C++ソフトウェアデザインなんかもお勧めです。 -- トゥイー 2013-03-30 (土) 10:55:50
お名前: