您的位置:首页 > 其它

TinyXML 指南

2013-01-10 15:02 225 查看

TinyXML中文指南

这是什么?

这份指南有一些关于如何有效地使用TinyXML的技巧和建议。

我也会尝试讲一些诸如怎样使字符串与整型数相互转化的C++技巧。这与TinyXML本身没什么关系,但它也许会对你的项目有所帮助,所以我还是把它加进来了。

如果你不知道基本的C++概念,那么这份指南就没什么用了。同样的,如果你不知道什么是DOM,那先从其它地方找来看看吧。

在我们开始之前

一些将会被用到的XML数据集/文件。

example1.xml:

<?xml version="1.0" ?>

<Hello>World</Hello>

example2.xml:

<?xml version="1.0" ?>

<poetry>

<verse>

Alas

Great World

Alas (again)

</verse>

</poetry>

example3.xml:

<?xml version="1.0" ?>

<shapes>

<circle name="int-based" x="20" y="30" r="50" />

<point name="float-based" x="3.5" y="52.1" />

</shapes>

example4.xml:

<?xml version="1.0" ?>

<MyApp>

<!– Settings for MyApp –>

<Messages>

<Welcome>Welcome to MyApp</Welcome>

<Farewell>Thank you for using MyApp</Farewell>

</Messages>

<Windows>

<Window name="MainFrame" x="5" y="15" w="400" h="250" />

</Windows>

<Connection ip="192.168.0.1" timeout="123.456000" />

</MyApp>

开始

把文件加载成XML

把一个文件加载成TinyXML DOM的最简单方法是:

TiXmlDocument doc( "demo.xml" );

doc.LoadFile();

一个更接近于现实应用的例子如下。它加载文件并把内容显示到标准输出STDOUT上:

// 加载指定的文件并把它的结构输出到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 ); // 稍后在指南中定义

}

else

{

printf("Failed to load file \"%s\”\n", pFilename);

}

}

在main中使用此函数的一个简单应用示范如下:

int main(void)

{

dump_to_stdout("example1.xml");

return 0;

}

回想example1的XML:

<?xml version="1.0" ?>

<Hello>World</Hello>

用这个XML运行程序就会在控制台/DOS窗口中显示:

DOCUMENT

+ DECLARATION

+ ELEMENT Hello

+ TEXT[World]

”dump_to_stdout“函数稍后会在这份指南中定义,如果你想要理解怎样递归遍历一个DOM它会很有用。

用程序建立文档对象

这是用程序建立example1的方法:

void build_simple_doc( )

{

// 生成xml: <?xml ..><Hello>World</Hello>

TiXmlDocument doc;

TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );

TiXmlElement * element = new TiXmlElement( "Hello" );

TiXmlText * text = new TiXmlText( "World" );

element->LinkEndChild( text );

doc.LinkEndChild( decl );

doc.LinkEndChild( element );

doc.SaveFile( "madeByHand.xml" );

}

然后可以用以下方法加载并显示在控制台上:

dump_to_stdout("madeByHand.xml"); // 此函数稍后会中指南中定义

你会看到跟example1一模一样:

madeByHand.xml:

Document

+ Declaration

+ Element [Hello]

+ Text: [World]

这段代码会产生相同的XML DOM,但它以不同的顺序来创建和链接结点:

void write_simple_doc2( )

{

// 实现与 write_simple_doc1一样的功能,(译注:我想它指是build_simple_doc)

// 但尽可能早地把结点添加到树中。

TiXmlDocument doc;

TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );

doc.LinkEndChild( decl );

TiXmlElement * element = new TiXmlElement( "Hello" );

doc.LinkEndChild( element );

TiXmlText * text = new TiXmlText( "World" );

element->LinkEndChild( text );

doc.SaveFile( "madeByHand2.xml" );

}

两个都产生同样的XML,即:

<?xml version="1.0" ?>

<Hello>World</Hello>

结构构成都是:

DOCUMENT

+ DECLARATION

+ ELEMENT Hello

+ TEXT[World]

属性

给定一个存在的结点,设置它的属性是很容易的:

window = new TiXmlElement( "Demo" );

window->SetAttribute("name", "Circle");

window->SetAttribute("x", 5);

window->SetAttribute("y", 15);

window->SetDoubleAttribute("radius", 3.14159);

你也可以用TiXmlAttribute对象达到同样的目的。

下面的代码向我们展示了一种(并不只有一种)获取某一元素属性并打印出它们的名字和字符串值的方法,如果值能够被转化为整型数或者浮点数,也把值打印出来:

// 打印pElement的所有属性。

// 返回已打印的属性数量。

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;

}

把文档对象写到文件中

把一个已经建立好的DOM写到文件中是非常简单的:

doc.SaveFile( saveFilename );

回想一下,比如example4:

<?xml version="1.0" ?>

<MyApp>

<!– Settings for MyApp –>

<Messages>

<Welcome>Welcome to MyApp</Welcome>

<Farewell>Thank you for using MyApp</Farewell>

</Messages>

<Windows>

<Window name="MainFrame" x="5" y="15" w="400" h="250" />

</Windows>

<Connection ip="192.168.0.1" timeout="123.456000" />

</MyApp>

以下函数建立这个DOM并把它写到“appsettings.xml”文件中:

void write_app_settings_doc( )

{

TiXmlDocument doc;

TiXmlElement* msg;

TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );

doc.LinkEndChild( decl );

TiXmlElement * root = new TiXmlElement( "MyApp" );

doc.LinkEndChild( root );

TiXmlComment * comment = new TiXmlComment();

comment->SetValue(" Settings for MyApp " );

root->LinkEndChild( comment );

TiXmlElement * msgs = new TiXmlElement( "Messages" );

root->LinkEndChild( msgs );

msg = new TiXmlElement( "Welcome" );

msg->LinkEndChild( new TiXmlText( "Welcome to MyApp" ));

msgs->LinkEndChild( msg );

msg = new TiXmlElement( "Farewell" );

msg->LinkEndChild( new TiXmlText( "Thank you for using MyApp" ));

msgs->LinkEndChild( msg );

TiXmlElement * windows = new TiXmlElement( "Windows" );

root->LinkEndChild( windows );

TiXmlElement * window;

window = new TiXmlElement( "Window" );

windows->LinkEndChild( window );

window->SetAttribute("name", "MainFrame");

window->SetAttribute("x", 5);

window->SetAttribute("y", 15);

window->SetAttribute("w", 400);

window->SetAttribute("h", 250);

TiXmlElement * cxn = new TiXmlElement( "Connection" );

root->LinkEndChild( cxn );

cxn->SetAttribute("ip", "192.168.0.1");

cxn->SetDoubleAttribute("timeout", 123.456); // 浮点数属性

dump_to_stdout( &doc );

doc.SaveFile( "appsettings.xml" );

}

dump_to_stdout函数将显示如下结构:

Document

+ Declaration

+ Element [MyApp]

(No attributes)

+ Comment: [ Settings for MyApp ]

+ Element [Messages]

(No attributes)

+ Element [Welcome]

(No attributes)

+ Text: [Welcome to MyApp]

+ Element [Farewell]

(No attributes)

+ Text: [Thank you for using MyApp]

+ Element [Windows]

(No attributes)

+ Element [Window]

+ name: value=[MainFrame]

+ x: value=[5] int=5 d=5.0

+ y: value=[15] int=15 d=15.0

+ w: value=[400] int=400 d=400.0

+ h: value=[250] int=250 d=250.0

5 attributes

+ Element [Connection]

+ ip: value=[192.168.0.1] int=192 d=192.2

+ timeout: value=[123.456000] int=123 d=123.5

2 attributes

TinyXML默认以其它APIs称作“pretty”格式的方式来输出XML,对此我感到惊讶。这种格式修改了元素的文本结点中的空格,以使输出来的结点树包含一个嵌套层标记。

我还没有仔细看当写到一个文件中时是否有办法关闭这种缩进——这肯定很容易做到。(译注:这两句话大概是Ellers说的)

[Lee:在STL模式下这很容易做到,只需要cout << myDoc就行了。在非STL模式下就总是用“pretty”格式了,加多一个开关是一个很好的特性,这已经被要求过了。]

XML与C++对象的相互转化

介绍

这个例子假设你在用一个XML文件来加载和保存你的应用程序配置,举例来说,有点像example4.xml。

有许多方法可以做到这点。例如,看看TinyBind项目:http://sourceforge.net/projects/tinybind

这一节展示了一种普通老式的方法来使用XML加载和保存一个基本的对象结构。

建立你的对象类

从一些像这样的基本类开始:

#include <string>

#include <map>

using namespace std;

typedef std::map<std::string,std::string> MessageMap;

// 基本的窗口抽象 - 仅仅是个示例

class WindowSettings

{

public:

int x,y,w,h;

string name;

WindowSettings()

: x(0), y(0), w(100), h(100), name("Untitled")

{

}

WindowSettings(int x, int y, int w, int h, const string& name)

{

this->x=x;

this->y=y;

this->w=w;

this->h=h;

this->name=name;

}

};

 

class ConnectionSettings

{

public:

string ip;

double timeout;

};

class AppSettings

{

public:

string m_name;

MessageMap m_messages;

list<WindowSettings> m_windows;

ConnectionSettings m_connection;

AppSettings() {}

void save(const char* pFilename);

void load(const char* pFilename);

// 仅用于显示它是如何工作的

void setDemoValues()

{

m_name="MyApp";

m_messages.clear();

m_messages["Welcome"]="Welcome to "+m_name;

m_messages["Farewell"]="Thank you for using "+m_name;

m_windows.clear();

m_windows.push_back(WindowSettings(15,15,400,250,"Main"));

m_connection.ip="Unknown";

m_connection.timeout=123.456;

}

};

这是一个基本的mian(),它向我们展示了怎样创建一个默认的settings对象树,怎样保存并再次加载:

int main(void)

{

AppSettings settings;

settings.save("appsettings2.xml");

settings.load("appsettings2.xml");

return 0;

}

接下来的main()展示了如何创建,修改,保存和加载一个settings结构:

int main(void)

{

// 区块:定制并保存settings

{

AppSettings settings;

settings.m_name="HitchHikerApp";

settings.m_messages["Welcome"]="Don’t Panic";

settings.m_messages["Farewell"]="Thanks for all the fish";

settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame"));

settings.m_connection.ip="192.168.0.77";

settings.m_connection.timeout=42.0;

settings.save("appsettings2.xml");

}

// 区块:加载settings

{

AppSettings settings;

settings.load("appsettings2.xml");

printf("%s: %s\n", settings.m_name.c_str(),

settings.m_messages["Welcome"].c_str());

WindowSettings & w=settings.m_windows.front();

printf("%s: Show window ’%s’ at %d,%d (%d x %d)\n",

settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h);

printf("%s: %s\n", settings.m_name.c_str(),

settings.m_messages["Farewell"].c_str());

}

return 0;

}

当save()和load()完成后(请看下面),运行这个main()就会在控制台看到:

HitchHikerApp: Don’t Panic

HitchHikerApp: Show window ‘BookFrame’ at 15,25 (300 x 100)

HitchHikerApp: Thanks for all the fish

把C++状态编码成XML

有很多方法能够做到把文档对象保存到文件中,这就是其中一个:

void AppSettings::save(const char* pFilename)

{

TiXmlDocument doc;

TiXmlElement* msg;

TiXmlComment * comment;

string s;

TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );

doc.LinkEndChild( decl );

TiXmlElement * root = new TiXmlElement(m_name.c_str());

doc.LinkEndChild( root );

comment = new TiXmlComment();

s=" Settings for "+m_name+" ";

comment->SetValue(s.c_str());

root->LinkEndChild( comment );

// 区块:messages

{

MessageMap::iterator iter;

TiXmlElement * msgs = new TiXmlElement( "Messages" );

root->LinkEndChild( msgs );

for (iter=m_messages.begin(); iter != m_messages.end(); iter++)

{

const string & key=(*iter).first;

const string & value=(*iter).second;

msg = new TiXmlElement(key.c_str());

msg->LinkEndChild( new TiXmlText(value.c_str()));

msgs->LinkEndChild( msg );

}

}

// 区块:windows

{

TiXmlElement * windowsNode = new TiXmlElement( "Windows" );

root->LinkEndChild( windowsNode );

list<WindowSettings>::iterator iter;

for (iter=m_windows.begin(); iter != m_windows.end(); iter++)

{

const WindowSettings& w=*iter;

TiXmlElement * window;

window = new TiXmlElement( "Window" );

windowsNode->LinkEndChild( window );

window->SetAttribute("name", w.name.c_str());

window->SetAttribute("x", w.x);

window->SetAttribute("y", w.y);

window->SetAttribute("w", w.w);

window->SetAttribute("h", w.h);

}

}

// 区块:connection

{

TiXmlElement * cxn = new TiXmlElement( "Connection" );

root->LinkEndChild( cxn );

cxn->SetAttribute("ip", m_connection.ip.c_str());

cxn->SetDoubleAttribute("timeout", m_connection.timeout);

}

doc.SaveFile(pFilename);

}

用修改过的main运行会生成这个文件:

<?xml version="1.0" ?>

<HitchHikerApp>

<!– Settings for HitchHikerApp –>

<Messages>

<Farewell>Thanks for all the fish</Farewell>

<Welcome>Don't Panic</Welcome>

</Messages>

<Windows>

<Window name="BookFrame" x="15" y="25" w="300" h="250" />

</Windows>

<Connection ip="192.168.0.77" timeout="42.000000" />

</HitchHikerApp>

从XML中解码出状态

就像编码一样,也有许多方法可以让你从自己的C++对象结构中解码出XML。下面的方法使用了TiXmlHandles。

void AppSettings::load(const char* pFilename)

{

TiXmlDocument doc(pFilename);

if (!doc.LoadFile()) return;

TiXmlHandle hDoc(&doc);

TiXmlElement* pElem;

TiXmlHandle hRoot(0);

// 区块:name

{

pElem=hDoc.FirstChildElement().Element();

// 必须有一个合法的根结点,如果没有则温文地处理(译注:直接返回)

if (!pElem) return;

m_name=pElem->Value();

// 保存起来以备后面之用

hRoot=TiXmlHandle(pElem);

}

// 区块:string table

{

m_messages.clear(); // 清空已有的table

pElem=hRoot.FirstChild( "Messages" ).FirstChild().Element();

for( pElem; pElem; pElem=pElem->NextSiblingElement())

{

const char *pKey=pElem->Value();

const char *pText=pElem->GetText();

if (pKey && pText)

{

m_messages[pKey]=pText;

}

}

}

// 区块:windows

{

m_windows.clear(); // 清空链表

TiXmlElement* pWindowNode=hRoot.FirstChild( "Windows" )

.FirstChild().Element();

for( pWindowNode; pWindowNode;

pWindowNode=pWindowNode->NextSiblingElement())

{

WindowSettings w;

const char *pName=pWindowNode->Attribute("name");

if (pName) w.name=pName;

pWindowNode->QueryIntAttribute("x", &w.x); // 如果失败,原值保持现状

pWindowNode->QueryIntAttribute("y", &w.y);

pWindowNode->QueryIntAttribute("w", &w.w);

pWindowNode->QueryIntAttribute("hh", &w.h);

m_windows.push_back(w);

}

}

// 区块:connection

{

pElem=hRoot.FirstChild("Connection").Element();

if (pElem)

{

m_connection.ip=pElem->Attribute("ip");

pElem->QueryDoubleAttribute("timeout",&m_connection.timeout);

}

}

}

dump_to_stdout的完整列表

下面是一个可直接运行的示例程序,使用上面提到过的递归遍历方式,可用来加载任意的XML文件并把结构输出到STDOUT上。

// 指南示例程序

#include "stdafx.h"

#include "tinyxml.h"

// ———————————————————————-

// STDOUT输出和缩进实用函数

// ———————————————————————-

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 ];

}

// 与getIndent相同,但最后没有“+”

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 );

}

}

// 加载指定的文件并把它的结构输出到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 );

}

else

{

printf("Failed to load file \"%s\”\n", pFilename);

}

}

// ———————————————————————-

// main(),打印出从命令行指定的文件

// ———————————————————————-

int main(int argc, char* argv[])

{

for (int i=1; i<argc; i++)

{

dump_to_stdout(argv[i]);

}

return 0;

}

从命令行或者DOS窗口运行它,例如:

C:\dev\tinyxml> Debug\tinyxml_1.exe example1.xml

example1.xml:

Document

+ Declaration

+ Element [Hello]

(No attributes)

+ Text: [World]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: