xml2htmlとは、xmlファイルをインデントとをつけHTMLファイルに変換する
プログラムで、samples/xml2html/にあります。
標準入力から読み込んで標準出力に結果を表示するので、
./xml2html < [入力XMLファイル] > [出力HTMLファイル]
のようにして使います。
これくらい簡単な操作だとXMLパーサを使わずとも、
正規表現置換なんかでできそうな気もしますが、MiXを使いましょう。
この例のようにドキュメントを上から順番に見て行く場合はDOMサブセットより、
SAXのほうが適しています。
SAXはイベント駆動型のAPIなので、使用するにはイベントを処理する、
イベントハンドラを記述する必要があります。
またイベントハンドラは、
MiX::SAX_EventHandler<charT,traitsT>
を継承する必要があります。
SAX_EventHandlerの型パラメータの意味は、
です。
traitsTは特に変わったことをしない限り、デフォルトで定義されているものを使え
ば問題ありません。
ので、イベントハンドラの宣言はこのようになります。
#include <iostream>
#include <MiX/MiX.h>
class EventHandler : public MiX::SAX_EventHandler<char>{
コンストラクタおよびメンバ変数は次のようになります。
int indent_;
std::ostream& out_;
public:
EventHandler(std::ostream& out) : out_(out){
indent_ = 0;
}
コンストラクタでは出力先のストリームへの参照をout_を受け取って、
indent_というインデント量を示すメンバ変数を0にセットしています。
では各イベントの処理を記述しましょう。
まず、XML宣言(<?xml .....?>のようなもの)を見つけた時に呼ばれる
onXMLDeclaration(AttrMap<char> attr)からです。
virtual void onXMLDeclaration(MiX::AttrMap<char> attr){
out_ << "<html>" << std::endl
<< "<head><title>xml2html</title></head>"
<< std::endl << "<body>" << std::endl;
}
特になにもせずにout_にHTMLヘッダを出力します。
次に、タグが開始した時に呼び出されるonStartを記述します。
virtual void onStart(MiX::XMLString<char> name,MiX::AttrMap<char> attr){
out_ << "<div style=\"margin-left:" << 40*indent_
<< "px\">" << "<b><" << name << "</b>" << std::endl;
MiX::AttrMap<char>::iterator it = attr.begin();
for( ;it!=attr.end();it++){
out_ << " " << it->first
<< " = <span style=\"color: #000055;\">\'"
<< it->second << "\'</span>" << std::endl;
}
out_ << "<b>></b>" << std::flush;
out_ << "</div>" << std::endl;
++indent_;
}
onStartの引数の意味は、
です。
attrはキーと値のペアを格納する辞書ですが、std::mapと違い順序を保存します。
onStartでは、indent_が示すインデント量に従ってタグ名を出力したあと、attrのイテレータを使って、attr内の全ての要素を出力しています。
そして、その後、メンバ変数indent_をインクリメントしています。
次にonEndを記述します。
virtual void onEnd(MiX::XMLString<char> name){
indent_--;
out_ << "<div style=\"margin-left:" << 40*indent_
<< "px\">" << "<b></" << name << "></b>"
<< "</div>";
if(indent_==0){
out_ << "</body></html>" << std::endl;
}
}
ここでは、indent_をデクリメントして、終了タグを出力しています。
また、indentが0ならパージングが終ったとみなしHTMLのフッタを出力しています。
では、次にonTextとonCommentを同時にみてみましょう。
virtual void onText(MiX::XMLString<char> text){
out_ << "<div style=\"margin-left:" << 40*indent_
<< "px\">" << text << "</div>" << std::endl;
}
virtual void onComment(MiX::XMLString<char> text){
out_ << "<div style=\"margin-left:" << 40*indent_
<< "px\">" << "<span style=\"color: #777777;\">"
<< "<!-" << text << "-->"
<< "</span>" << "</div>" << std::endl;
}
};
onTextとonCommentはindent_に従ってインデントをしたデータを出力しているだけです。
これでイベント駆動部分の実装は終りました。
これだけで、ほとんどの部分は完成しています。
あとは、パージングを開始するまでの処理をmainに書いておわりです。
最後にわずかなmainの実装です。
int main(int argc,char* argv[]){
try{
MiX::SAX_Parser<char> parser;
EventHandler handler(std::cout);
parser.setIgnoreSpace(true);
parser.setEventHandler(&handler);
parser.parse(std::cin);
return 0;
SAX_Parserを生成し、setIgnoreSpaceで空白文字を無視することを指定し、
parse(std::cin)で、標準入力からパースさせています。
これで先程作ったイベントハンドラが順番に呼び出されます。
パースが失敗した時、SAX_Parser::parseは例外を投げるので、
catchブロックを書く必要があります。
}catch(MiX::ParsingException& e){
std::cerr << e.what() << std::endl;
return -1;
}
}
ここでは、エラー文字列を標準エラー出力に出力しているだけです。
以上でxml2htmlの実装は終了です。
このxml2htmlは空タグの終了タグが省略形にならなかったりとしますが、
非常に簡単に実装できました。
このシンプルな実装がSAXの強みです。(Simple API for XMLですんで)