TinyXML + babelを使ってみる。(3) -シリアライズ-
前回(TinyXML + babelを使ってみる。(2) -Visitorでトラバース- - while( c++ );)の続き。
今回はシリアライズ、デシリアライズについて勉強します。
といっても、XMLファイルへの出力にはまだ触れていないので、デシリアライズのみ扱います。
以下のようなXMLとクラスがある時、
<?xml version="1.0" ?> <test> <hoge a="1" b="2.0" c="ほげほげ" /> </test>
class Hoge { private: int a; float b; std::string c; };
ISerializableインターフェイスを実装し、属性(Attribute)からHogeクラスのデシリアライズを行います。
デシリアライズ時に直接XMLの操作をしたくないですし、XML以外のファイルからのデシリアライズも考慮して、IArchiverインターフェイスを使って間接的に属性を取得します。
class ISerializable { public: //virtual void serialize( IArchiver* ar ) = 0; virtual void deserialize( IArchiver* ar ) = 0; };
class IArchiver { public: virtual bool get_int( const std::string& name, int& value )const = 0; virtual bool get_float( const std::string& name, float& value )const = 0; virtual bool get_string( const std::string& name, std::string& value )const = 0; virtual void set( const std::string& name, const std::string& value ) = 0; };
class Hoge : public ISerializable { public: void deserialize( IArchiver* ar ) { ar->get_int( "a", a ); ar->get_float( "b", b ); ar->get_string( "c", c ); } private: int a; float b; std::string c; };
IArchiver実装クラス
とりあえず、属性リストをstd::mapでテーブル化しておきます。
class Archiver : public IArchiver { typedef std::map< std::string, std::string > table_t; public: bool get_int( const std::string& name, int& value )const { return get( name, value ); } bool get_float( const std::string& name, float& value )const { return get( name, value ); } bool get_string( const std::string& name, std::string& value )const { return get( name, value ); } void set( const std::string& name, const std::string& value ) { table[ name ] = value; } private: template< typename T > bool get( const std::string& name, T& value ) { table_t::iterator it = table.find( name ); if( it != table.end() ) { std::stringstream ss( it->second ); ss >> value; return true; } return false; } private: table_t table; };
Hogeクラス
Archiverの属性テーブルから値を取得してメンバに設定します。
class Hoge : public ISerializable { public: void deserialize( IArchiver* ar ) { ar->get_int( "a", a ); ar->get_float( "b", b ); ar->get_string( "c", c ); } public: void func() { std::cout << "a=" << a << std::endl << "b=" << b << std::endl << "c=" << c << std::endl ; } private: int a; float b; std::string c; };
デシリアライズ用Visitor
hoge要素を訪れた際に属性テーブルを生成し、Hogeクラスのデシリアライズを行います。
class Visitor : public TiXmlVisitor { public: /// Visit an element. virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { std::string name = element.ValueStr(); //属性テーブルの作成 Archiver ar; create_attribute_table( firstAttribute, &ar ); //Hogeのでシリアライズ if( name == "hoge" ) { hoge->deserialize( &ar ); } return true; } private: void create_attribute_table( const TiXmlAttribute* a, IArchiver* ar ) { while( a ) { ar->set( a->Name(), babel::utf8_to_sjis( a->ValueStr() ) ); a = a->Next(); } } public: Visitor( Hoge* hoge ) : hoge( hoge ) {} private: Hoge* hoge; };
使ってみる
int main() { babel::init_babel(); Hoge hoge; TiXmlDocument doc( XMLファイルを指定 ); if( doc.LoadFile() ) { Visitor visitor( &hoge ); doc.Accept( &visitor ); } hoge.func(); return 0; }
実行結果
a=1 b=2 c=ほげほげ
テキストノードの取得
以下のようにテキストノードとメンバの文字列を対応させるには、親要素でテキストを取得してテキストノードへのVisitを行わないように、Visitメソッドでfalseを返します。
<?xml version="1.0" ?> <test> <hoge a="1" b="2.0" c="ほげほげ" /> <piyo>ぴよぴよ</piyo> </test>
class Piyo { std::string text; };
class Visitor : public TiXmlVisitor { public: /// Visit an element. virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { std::string name = element.ValueStr(); //属性テーブルの作成 Archiver ar; create_attribute_table( firstAttribute, &ar ); //各オブジェクトのデシリアライズ if( name == "hoge" ) { hoge->deserialize( &ar ); } if( name == "piyo" ) { //テキストノードの取得 get_text( element.FirstChild(), &ar ); piyo->deserialize( &ar ); return false; //子ノードのVisitを行わない } return true; } void get_text( const TiXmlNode* text, IArchiver* ar ) { if( text != 0 && text->Type() == TiXmlNode::TEXT ) { ar->set_text( babel::utf8_to_sjis( text->ValueStr() ) ); } } ... };
IArchiverインターフェイス
とりあえずテキストノードの取得、設定が出来るようにメソッドを追加しておきます。
class IArchiver { public: ... virtual std::string get_text()const = 0; virtual void set_text( const std::string& text ) = 0; };
IArchiver実装クラス
class Archiver : public IArchiver { public: ... std::string get_text()const { return text; } void set_text( const std::string& text ) { this->text = text; } private: std::string text; };
Piyoクラス
class Piyo : public ISerializable { public: void deserialize( IArchiver* ar ) { text = ar->get_text(); } public: void func() { std::cout << "text=" << text << std::endl; } public: Piyo() : text( "" ) {} private: std::string text; };
使ってみる
int main() { babel::init_babel(); Hoge hoge; Piyo piyo; TiXmlDocument doc( XMLファイルを指定 ); if( doc.LoadFile() ) { Visitor visitor( &hoge, &piyo ); doc.Accept( &visitor ); } hoge.func(); piyo.func(); return 0; }
実行結果
a=1 b=2 c=ほげほげ text=ぴよぴよ
サンプル
- tinyxml_03.zip
- VC++2008EEで動作確認
- tinyxmlを使用しています
- babelを使用しています