XMLを処理するのに…
Windows で C++ にて XML を処理しようと思ったら、考えられそうな選択肢がいくつかあります。
- LibXML2
- Expat
- MSXML
- TinyXML
- XMLLite
- Xerces-C++
そこで、なるべくならプラットフォームを固定して書きたくないので、MSXML と XMLLite が候補から外れます。
LibXML2 でやろうとしたら、ICU か iconv あたりをなんとかしないといけません。正直 iconv はmsvc でコンパイルするのが困難です。libiconv for windows ありますが、vc で、すんなりコンパイルが通りません。じゃあ ICU だわさ?って、ICUを使おうとすると、icu.lib が無いとか変なリンクエラーが出て萎えてしまいます。ICU も大げさな感じがして、使うのをちょっと躊躇われるのという事情もあります。
おまえ、選り好みが激しすぎと怒られそうですが、Xerces-C++ も、なんか、あんまり好きじゃないんです。
TinyXML もいいんですけど、ちょっと考えてしまう…(こんな事ばかり書いてると、いい加減に袋叩きにあいそう)。
そうすると残されたのは、Expat って事になります。ところが、この Expat って、SJIS に対応していないんですよね…。APIで日本語対応するって事も可能なんですが、なんというか、どうもしっくり来ない。XMLの構文を解析するついでに、文字コード変換もやりましょうという思想なんで、1文字か2文字づつコード変換の処理をしなきゃならない。これ、文字コード変換ライブラリのメンテナンスも考えたら、結構大変です。SJIS に限って言えば、SJIS第一バイトの場合は2バイト処理します。なんつーか、もやもや~とするんですよねー。
で、結論は何かと言うと、そもそも wchar_t が嫌い。これに尽きるんですわ。UTF-8 は、ほんと良く出来てると思います。
結局、自分の好みでやってくと、「文字コード変換」と「expatで遊ぼ」を組み合わせて、こんな感じになってしまいました。ただ、encoding の指定が全然関係ないやん…って部分が気に入りません。
#include <iostream>
#include <string>
#include <vector>
#include <expat.h>
#include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp>
#include "codepage.hpp"
#include "wconv.hpp"
class xml_node;
class xml_node {
public:
xml_node( const XML_Char* name ) : node_name_(name) {}
public:
std::basic_string<XML_Char> node_name_;
std::basic_string<XML_Char> node_value_;
std::vector< boost::shared_ptr<xml_node> > childs_;
};
class xml_holder {
public:
std::vector< boost::shared_ptr<xml_node> > stack_;
boost::shared_ptr<xml_node> current_;
};
void starter(void* userData, const XML_Char* name, const XML_Char* attrs[]) {
xml_holder* holder = reinterpret_cast<xml_holder*>(userData);
holder->stack_.push_back( holder->current_ );
boost::shared_ptr<xml_node> element( new xml_node( name ) );
holder->current_->childs_.push_back( element );
holder->current_ = element;
}
void ender( void* userData, const XML_Char* name) {
xml_holder* holder = reinterpret_cast<xml_holder*>(userData);
holder->current_ = holder->stack_.back();
holder->stack_.pop_back();
}
void data( void* userData, const XML_Char* s, int len ) {
xml_holder* holder = reinterpret_cast<xml_holder*>(userData);
holder->current_->node_value_.assign( s, s + len );
}
void trace( int depth, boost::shared_ptr<xml_node> elem ) {
for( int i = 0; i < depth; ++i ) { std::cout << " "; }
std::cout << elem->node_name_ << ":" << code_to_code<CP_ACP,CP_UTF8>(elem->node_value_) << std::endl;
BOOST_FOREACH( boost::shared_ptr<xml_node> e, elem->childs_ ) {
trace( depth+1, e );
}
}
bool parse_xml( const std::string& xml ) {
xml_holder holder;
holder.current_ = boost::shared_ptr<xml_node>( new xml_node( "root" ) );
XML_Parser parser = XML_ParserCreate("UTF-8");
if( !parser ) return false;
XML_SetUserData( parser, &holder );
XML_SetElementHandler( parser, starter, ender );
XML_SetCharacterDataHandler( parser, data );
int iseof = 0;
XML_Parse( parser, xml.c_str(), xml.size(), iseof );
XML_ParserFree( parser );
trace( 0, holder.current_ );
return !iseof;
}
#include <fstream>
int main(int argc, char* argv[] ) {
if( argc != 2 ) return 1;
std::ifstream ifs( argv[1] );
std::string xml;
std::string line;
while( ifs.good() ) {
std::getline( ifs, line );
xml += line;
//std::cout << line << std::endl;
}
//std::cout << std::endl;
parse_xml( code_to_code<CP_UTF8,CP_ACP>(xml) );
return 0;
}
コメントを残す