您的位置:首页 > 其它

TinyXml-2 Tutorial 中文翻译

2017-08-22 16:26 363 查看
原文地址 http://www.grinninglizard.com/tinyxmldocs/tutorial0.html

什么是TinyXml?

该教程会包含一些怎么高效使用TinyXML的提示和建议。

本文将包含一些C++的小技巧例如转换字符串到整型或反过来。这些技巧和TinyXML本身没有啥联系,但是也许会给你的project些许帮助。

如果你不清楚基本的C++概念那么这篇教程就对你没啥用。同样的如果你对DOM是什么感到模糊,建议先查阅一下DOM的资料。

W3C的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="40
4000
0" h="250" />
</Windows>
<Connection ip="192.168.0.1" timeout="123.456000" />
</MyApp>


开始教程

从文件中载入XML

最简单的载入文件为TinyXML DOM的方式为:

TiXmlDocument doc( "demo.xml" );
doc.LoadFile();


一个更现实点的方式如下。该方式是将会载入该文件并该XML打印到STDOUT:

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


一个使用dump_to_stdout函数的演示如下:

int main(void)
{
dump_to_stdout("example1.xml");
return 0;
}


EXAMPLE 1 XML 是:

<?xml version="1.0" ?>
<Hello>World</Hello>


运行上面的main函数将会打印以下内容到 控制台/DOS窗口:

DOCUMENT
+ DECLARATION
+ ELEMENT Hello
+ TEXT[World]


dump_to_stdout
函数将会在下文定义,该函数会让你更好理解如何递归遍历DOM。

如何通过代码建立一个XML文档

以下将演示如何通过代码建立 Example 1。

void build_simple_doc( )
{
// Make xml: <?xml ..><Hello>World</Hello>
TiXmlDocument do c;
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" );
}


生成的XML可被如下函数载入并显示到控制台:

dump_to_stdout("madeByHand.xml"); // this func defined later in the tutorial


你会发现打印结果和 Example 1是一样的:

madeByHand.xml:
Document
+ Declaration
+ Element [Hello]
+ Text: [World]


下述代码可生成完全相同的XML DOM,但是他的节点NODE建立和连接顺序并不相同(其实就是一个先全部定义后再连接,下面这个是一建立后就连接):

void write_simple_doc2( )
{
// same as write_simple_doc1 but add each node
// as early as possible into the tree.

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

Both of these produce the same XML, 也就是说:

<?xml version="1.0" ?>
<Hello>World</Hello>


或者描述成以下格式:

DOCUMENT
+ DECLARATION
+ ELEMENT Hello
+ TEXT[World]


Attributes(属性)

给出一个已存在的节点NODE,设置它的属性是非常容易的:

window = new TiXmlElement( "Demo" );
window->SetAttribute("name", "Circle");
window->SetAttribute("x", 5);
window->SetAttribute("y", 15);
window->SetDoubleAttribute("radius", 3.14159);


你也可以使用TiXmlAttribute对象来完成操作。

以下代码演示了得到一个Element(元素)所有的Attributes(属性),并打印它们的 name(名称)和string(字符串)值,并且如果该值可由一个int转换成double值,把该double值也打印出来。

// print all attributes of pElement.
// returns the number of attributes printed
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;
}


将XML文档写入到文件中

将一个提前建立好的DOM写到文件里是很简单的:

doc.SaveFile( saveFilename );


回忆 example 4:

<?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); // floating point attrib

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默认情况下就可以写出让其他API称赞为“完美”格式的XML排版-它调整了element文本(TEXT)的空格数量,从而让它看起来包含了其他节点Node,这样的话在写出其树形结构的时候可以非常清晰的看出其嵌套结构。

我还没有看到在编写文件时是否有办法关闭缩进,但这是很容易实现的。

[Lee(原文作者): 使用STL模式是很简单的,仅仅只需要cout << myDoc. NON-STL 模式 总是会是”完美” 格式。 添加一个 switch 将会是一个非常棒的featrue,并且也是这样要求的.]

以下待翻译~~~~~~~~~~~~~~~~~~~~

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;

// a basic window abstraction - demo purposes only
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);

// just to show how to do it
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;
}
};


这个基础的main()展示了怎么建立一个默认的设置对象树(default settings object tree),再次保存并载入它:

int main(void)
{
AppSettings settings;

settings.save("appsettings2.xml");
settings.load("appsettings2.xml");
return 0;
}


接下来的main()展示了如何使用设置结构体(settings structure)创建、修改、保存、载入:

int main(void)
{
// block: customise and save 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");
}

// block: load 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++状态(state)代码编码至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 );

// block: 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 );
}
}

// block: 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);
}
}

// block: 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>


将状态(state)从XML解码至C++

有许多种方法将一个已编码XML对象解码至C++对象结构体。以下方法使用了TiXmlHandles。

void AppSettings::load(const char* pFilename)
{
TiXmlDocument doc(pFilename);
if (!doc.LoadFile()) return;

TiXmlHandle hDoc(&doc);
TiXmlElement* pElem;
TiXmlHandle hRoot(0);

// block: name
{
pElem=hDoc.FirstChildElement().Element();
// should always have a valid root but handle gracefully if it does
if (!pElem) return;
m_name=pElem->Value();

// save this for later
hRoot=TiXmlHandle(pElem);
}

// block: string table
{
m_messages.clear(); // trash existing 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;
}
}
}

// block: windows
{
m_windows.clear(); // trash existing list

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); // If this fails, original value is left as-is
pWindowNode->QueryIntAttribute("y", &w.y);
pWindowNode->QueryIntAttribute("w", &w.w);
pWindowNode->QueryIntAttribute("hh", &w.h);

m_windows.push_back(w);
}
}

// block: 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的演示例子

// tutorial demo program
#include "stdafx.h"
#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); } }

// ----------------------------------------------------------------------
// main() for printing files named on the command line
// ----------------------------------------------------------------------
int main(int argc, char* argv[])
{
for (int i=1; i<argc; i++)
{
dump_to_stdout(argv[i]);
}
return 0;
}
Run this from the command line or a DOS window, e.g.:

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

example1.xml:
Document
+ Declaration
+ Element [Hello]
(No attributes)
+ Text: [World]


Authors and Changes

Written by Ellers, April, May, June 2005

Minor edits and integration into doc system, Lee Thomason September 2005

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