TinyXML + babelを使ってみる。(2) -Visitorでトラバース-
前回(TinyXML + babelを使ってみる。(1) - while( c++ );)の続き。
XMLファイルからDOMツリーを構築
TiXmlDocument doc(pFilename); bool loadOkay = doc.LoadFile(); if (loadOkay) { ... } else { //Failed to load file }
メモリからDOMツリーを構築
TiXmlDocument doc; const char* str = "<?xml version=\"1.0\" ?>" "<Hello>World</Hello>" ; doc.Parse( str ); bool loadOkay = !doc.Error(); if (loadOkay) { ... } else { //Failed to load file }
以上のように、XMLファイルまたはメモリからDOMツリーの構築が可能です。
前回はルートノードから再帰的にノードの情報を出力する関数を作りましたが(コピペ)、
今回はいわゆるVisitorパターンを使ってコンソールに出力してみます。
「Visitorさん」がノードを巡りながら「要素名、テキスト」を叫びます。
Visitorパターンに関してはGoogle先生に聞いてみて下さい。
TiXmlVisitor
TinyXMLにはDOMツリーをトラバースするためのVisitorクラスが用意されています。
class TiXmlVisitor { public: virtual ~TiXmlVisitor() {} /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } /// Visit a declaration virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } /// Visit a text node virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } /// Visit a comment node virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } /// Visit an unknow node virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } };
DOMツリーは6種類のノードで構成されています。
- TiXmlNode
- TiXmlDocument
- TiXmlElement
- TiXmlDeclaration
- TiXmlText
- TiXmlComment
- TiXmlUnknown
「要素名」と「テキスト」を出力するためには、以下の2つメソッドをオーバーライドしたVisitorクラスを作ります。
- bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ );
- bool Visit( const TiXmlText& /*text*/ );
class Visitor : public TiXmlVisitor { public: /// Visit an element. virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* /*firstAttribute*/ ) { std::cout << element.ValueStr() << std::endl; return true; } /// Visit a text node virtual bool Visit( const TiXmlText& text ) { std::cout << babel::utf8_to_sjis( text.ValueStr() ) << std::endl; return true; } };
各ノードはVisitorを受け入れるためのAcceptメソッドを実装しているので、ルートのTiXmlDocumentからAcceptを呼び出せば、自動的に全てのノードを巡りつつ、オーバーライドしたメソッドを呼び出してくれます。
int main() { babel::init_babel(); TiXmlDocument doc( "test.xml" ); if( doc.LoadFile() ) { Visitor visitor; doc.Accept( &visitor ); } return 0; }
実行結果
test hoge piyo ぴよぴよ
さらに前回と同様の結果になるよう修正してみます。
class Visitor : public TiXmlVisitor { private: enum{ NUM_INDENTS_PER_SPACE = 2 }; const char * getIndent( unsigned int numIndents ) { static const char * pINDENT=" + "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } // same as getIndent but no "+" at the end const char * getIndentAlt( unsigned int numIndents ) { static const char * pINDENT=" "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } public: /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { std::cout << "Document" << std::endl; indent ++; return true; } /// Visit a document. virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { indent --; return true; } /// Visit an element. virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { std::cout << getIndent( indent ); std::cout << "Element: [" << element.ValueStr() << "]" << std::endl; int count = visit_attributes( firstAttribute ); if( count == 0 ) std::cout << " (No attributes)"; else { // "1 attribute" std::cout << getIndentAlt(indent) << count << " attribute"; // "n attributes" if( count > 1 )std::cout << "s"; } std::cout << std::endl; indent ++; return true; } /// Visit an element. virtual bool VisitExit( const TiXmlElement& /*element*/ ) { indent --; return true; } /// Visit a declaration virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { std::cout << getIndent( indent ); std::cout << "Declaration" << std::endl; return true; } /// Visit a text node virtual bool Visit( const TiXmlText& text ) { std::cout << getIndent( indent ); std::cout << "Text: [" << babel::utf8_to_sjis( text.ValueStr() ) << "]" << std::endl; return true; } /// Visit a comment node virtual bool Visit( const TiXmlComment& comment ) { std::cout << getIndent( indent ); std::cout << "Comment: [" << comment.ValueStr() << "]" << std::endl; return true; } /// Visit an unknow node virtual bool Visit( const TiXmlUnknown& unknown ) { std::cout << getIndent( indent ); std::cout << "Unknown: [" << unknown.ValueStr() << "]" << std::endl; return true; } private: int visit_attributes( const TiXmlAttribute* a ) { int count = 0; while( a ) { std::cout << getIndent( indent + 1 ); std::cout << a->Name() << ": " << babel::utf8_to_sjis( a->ValueStr() ); int ival = 0; double dval = 0; if( a->QueryIntValue( &ival ) == TIXML_SUCCESS ) std::cout << " int=" << ival; if( a->QueryDoubleValue( &dval ) == TIXML_SUCCESS ) std::cout << " double=" << dval; std::cout << std::endl; a = a->Next(); count ++; } return count; } public: Visitor() : indent( 0 ) {} private: int indent; };
実行結果
Document + Declaration + Element: [test] (No attributes) + Element: [hoge] + a: 1 int=1 double=1 + b: 2.0 int=2 double=2 + c: ほげほげ 3 attributes + Element: [piyo] (No attributes) + Text: [ぴよぴよ]
サンプル
- tinyxml_01.zip
- VC++2008EEで動作確認。
- tinyxmlとbabelを使っています。
- tinyxmlではTIXML_USE_STLを指定してstd::stringを使っています。