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