TinyXML + babelを使ってみる。(1)

TinyXML

C++で作られたシンプルで軽量なXMLパーサです。速度は速くは無いですが、扱いやすいライブラリです。

babel

C++で作られた各種文字コードシフトJIS, JIS, EUC, UNICODE(UTF8, UTF16, UTF32) )変換ライブラリです。

以上のライブラリを使って、XMLの実験をします。
最終的にはWPFXAMLのように、アプリケーションの外観の設定やボタンの配置など出来たらいいなと。

テスト用XML
<?xml version="1.0" ?>
<app>
  <window title="title" w="720" h="480" >
    <button x="10" y="10" w="100" h="30">ボタン</button>
  </window>
  <window title="たいとる" w="320" h="240" />
</app>
まずはチュートリアル通りdumpしてみる

一番下にあるソースをコピペします。

#include "tinyxml.h"

// ----------------------------------------------------------------------
// STDOUT dump and indenting utility functions
// ----------------------------------------------------------------------
const unsigned int 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 ];
}

int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent)
{
	if ( !pElement ) return 0;

	TiXmlAttribute* pAttrib=pElement->FirstAttribute();
	int i=0;
	int ival;
	double dval;
	const char* pIndent=getIndent(indent);
	printf("\n");
	while (pAttrib)
	{
		printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value());

		if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS)    printf( " int=%d", ival);
		if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval);
		printf( "\n" );
		i++;
		pAttrib=pAttrib->Next();
	}
	return i;	
}

void dump_to_stdout( TiXmlNode* pParent, unsigned int indent = 0 )
{
	if ( !pParent ) return;

	TiXmlNode* pChild;
	TiXmlText* pText;
	int t = pParent->Type();
	printf( "%s", getIndent(indent));
	int num;

	switch ( t )
	{
	case TiXmlNode::DOCUMENT:
		printf( "Document" );
		break;

	case TiXmlNode::ELEMENT:
		printf( "Element [%s]", pParent->Value() );
		num=dump_attribs_to_stdout(pParent->ToElement(), indent+1);
		switch(num)
		{
			case 0:  printf( " (No attributes)"); break;
			case 1:  printf( "%s1 attribute", getIndentAlt(indent)); break;
			default: printf( "%s%d attributes", getIndentAlt(indent), num); break;
		}
		break;

	case TiXmlNode::COMMENT:
		printf( "Comment: [%s]", pParent->Value());
		break;

	case TiXmlNode::UNKNOWN:
		printf( "Unknown" );
		break;

	case TiXmlNode::TEXT:
		pText = pParent->ToText();
		printf( "Text: [%s]", pText->Value() );
		break;

	case TiXmlNode::DECLARATION:
		printf( "Declaration" );
		break;
	default:
		break;
	}
	printf( "\n" );
	for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) 
	{
		dump_to_stdout( pChild, indent+1 );
	}
}

// load the named file and dump its structure to STDOUT
void dump_to_stdout(const char* pFilename)
{
	TiXmlDocument doc(pFilename);
	bool loadOkay = doc.LoadFile();
	if (loadOkay)
	{
		printf("\n%s:\n", pFilename);
		dump_to_stdout( &doc ); // defined later in the tutorial
	}
	else
	{
		printf("Failed to load file \"%s\"\n", pFilename);
	}
}

int main()
{
    dump_to_stdout( "test.xml" );
    return 0;
}
実行結果

日本語部分は文字化けしてますが、それ以外は当然問題無いですね。

test.xml:
Document
+ Declaration
+ Element [app]
 (No attributes)
  + Element [window]
    + title: value=[title]
    + w: value=[720] int=720 d=720.0
    + h: value=[480] int=480 d=480.0
    3 attributes
    + Element [button]
      + x: value=[10] int=10 d=10.0
      + y: value=[10] int=10 d=10.0
      + w: value=[100] int=100 d=100.0
      + h: value=[30] int=30 d=30.0
      4 attributes
      + Text: [繝懊ち繝ウ]
  + Element [window]
    + title: value=[縺溘>縺ィ繧犠
    + w: value=[320] int=320 d=320.0
    + h: value=[240] int=240 d=240.0
    3 attributes
UTF-8シフトJIS変換

TinyXMLは文字コードの変換に対応していないので、今回はbabelを使って変換してみます。init_babel()で初期化してから、utf8_to_sjis()を使って変換します。

#include "babel.h"


int main()
{
    babel::init_babel();

    ..略..

    return 0;
}
  • テキストノードの取得
std::string str = babel::utf8_to_sjis( pText->Value() );
  • 属性の取得
std::string str = babel::utf8_to_sjis( pAttrib->Value() );
実行結果

正しく変換できました。

test.xml:
Document
+ Declaration
+ Element [app]
 (No attributes)
  + Element [window]
    + title: value=[title]
    + w: value=[720] int=720 d=720.0
    + h: value=[480] int=480 d=480.0
    3 attributes
    + Element [button]
      + x: value=[10] int=10 d=10.0
      + y: value=[10] int=10 d=10.0
      + w: value=[100] int=100 d=100.0
      + h: value=[30] int=30 d=30.0
      4 attributes
      + Text: [ボタン]
  + Element [window]
    + title: value=[たいとる]
    + w: value=[320] int=320 d=320.0
    + h: value=[240] int=240 d=240.0
    3 attributes