TinyXML Tutorial 中文指南

注:  本文是 TinyXML 2.5.3 版本 Document 中的《TinyXML Tutorial》的翻译文档,由本人 Dennis.Gao 翻译,版权归原作者所有,转载本文档请注明出处。原文出自 TinyXML 源码包doc目录。在线文档:http://www.grinninglizard.com/tinyxmldocs/tutorial0.html
Author : Dennis.Gao
Date   : 2008.01.01

这是什么?

本指南就如何有效的使用 TinyXML 提供一些窍门和建议。
这里也会包括一些 C++ 的窍门,像如何在字符串和整数之间进行转换。这和 TinyXML 本身并没有任何关系,但是它会对你的工程有所帮助,所以我把它写了进来。
如果你不知道 C++ 的基本概念,那么本指南对你没有任何用处。同样,如果你不知道 DOM 是什么,先在别的地方学习一下吧。

开始之前

一些 XML 数据集/文件将会被用到:
example1.xml:
`<?xmlversion="1.0"?>

<Hello>World</Hello>
`
example2.xml:
`<?xmlversion="1.0"?>

<poetry>

<verse>

Alas

Great World

Alas(again)

</verse>

</poetry>
`
example3.xml:
`<?xmlversion="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:
`<?xmlversion="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();
`

下面是一个更实际的用法。它会加载文件并在标准输出中显示文件内容:

`// load the named file and dump its structure to STDOUT



voiddump_to_stdout(constchar*pFilename)

{

TiXmlDocument doc(pFilename);

boolloadOkay=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() 函数中使用这个函数的简单示例:

`intmain(void)

{

dump_to_stdout("example1.xml");

return0;

}
`
Example 1  的 XML 是:
`<?xmlversion="1.0"?>

<Hello>World</Hello>
`
运行这个程序就可以将 XML 文件显示在控制台或 DOS 窗口中:
`DOCUMENT

+DECLARATION

+ELEMENT Hello

+TEXT[World]
`
在本指南的后面会给出 dump_to_stdout() 函数的定义,它对你了解如何递归遍历一个 DOM 十分有用。

通过编程建立 XML 文档

下面的函数可以建立 Example 1 文档:
`voidbuild_simple_doc()

{

// Make xml: World



TiXmlDocument doc;

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

TiXmlElement*element=newTiXmlElement("Hello");

TiXmlText*text=newTiXmlText("World");

element->LinkEndChild(text);

doc.LinkEndChild(decl);

doc.LinkEndChild(element);

doc.SaveFile("madeByHand.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 :
`voidwrite_simple_doc2()

{

// same as write_simple_doc1 but add each node



// as early as possible into the tree.





TiXmlDocument doc;

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

doc.LinkEndChild(decl);



TiXmlElement*element=newTiXmlElement("Hello");

doc.LinkEndChild(element);



TiXmlText*text=newTiXmlText("World");

element->LinkEndChild(text);



doc.SaveFile("madeByHand2.xml");

}
`
这两段代码生成的是同一段 XML ,如下:
`<?xmlversion="1.0"?>

<Hello>World</Hello>
`

也就是这种结构形式:

`DOCUMENT

+DECLARATION

+ELEMENT Hello

+TEXT[World]
`

属性

很简单就可以给一个已经存在的节点设置属性:

`window=newTiXmlElement("Demo");

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

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

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

window->SetDoubleAttribute("radius",3.14159);
`
你也可以使用 TiXmlAttribute 对象来做这件事。
下面这段代码给出了一种(并非仅此一种)如何得到一个元素的所有属性,然后打印出名字和字符串值的方法,并且,如果这些值可以转换成整形或浮点型,那么把他们也打印出来:
`// print all attributes of pElement.



// returns the number of attributes printed



intdump_attribs_to_stdout(TiXmlElement*pElement,unsignedintindent)

{

if(!pElement)return0;
`

TiXmlAttribute*pAttrib=pElement->FirstAttribute();

inti=0;

intival;

doubledval;

constchar*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();

}

returni;

}

将文档写入文件

将建立好的DOM写入一个文件很简单:

`doc.SaveFile(saveFilename);`
回想一下 example 4:
`<?xmlversion="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" 的文件中:
`voidwrite_app_settings_doc()

{

TiXmlDocument doc;

TiXmlElement*msg;

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

doc.LinkEndChild(decl);



TiXmlElement*root=newTiXmlElement("MyApp");

doc.LinkEndChild(root);



TiXmlComment*comment=newTiXmlComment();

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

root->LinkEndChild(comment);



TiXmlElement*msgs=newTiXmlElement("Messages");

root->LinkEndChild(msgs);



msg=newTiXmlElement("Welcome");

msg->LinkEndChild(newTiXmlText("Welcome to MyApp"));

msgs->LinkEndChild(msg);



msg=newTiXmlElement("Farewell");

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

msgs->LinkEndChild(msg);



TiXmlElement*windows=newTiXmlElement("Windows");

root->LinkEndChild(windows);



TiXmlElement*window;

window=newTiXmlElement("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=newTiXmlElement("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 ,它修改元素文字的空白,然后用嵌套层次的方式显示这棵树。
我没有注意到在写文件的时候是否有办法关掉缩排,但是这肯定很容易。
[ Lee:在 STL 模式下很容易,只要使用 cout << myDoc 就可以了。非 STL 模式通常是“优美”格式的。加入一个“开关”会是一个不错的特性,并且已经这么做了。]

XML 与 C++ 对象之间的转换

介绍

这个示例假定你正要加载你的应用设置并把它们保存在 XML 文件中,就像 example4.xml 那样。
有许多办法可以完成这件事。例如,看一看 TinyBind 这个工程,你可以在这里找到它:http://sourceforge.net/projects/tinybind
本节给出一个使用 XML 来加载并保存一个基本的对象结构的浅显易懂的方法。

建立你的对象类

先从下面的这些基类入手:

`#include<string>

#include<map>

usingnamespacestd;



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



// a basic window abstraction - demo purposes only



classWindowSettings

{

public:

intx,y,w,h;

stringname;



WindowSettings()

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

{

}



WindowSettings(intx,inty,intw,inth,conststring&name)

{

this->x=x;

this->y=y;

this->w=w;

this->h=h;

this->name=name;

}

};



classConnectionSettings

{

public:

stringip;

doubletimeout;

};



classAppSettings

{

public:

stringm_name;

MessageMap m_messages;

list<WindowSettings>m_windows;

ConnectionSettings m_connection;



AppSettings(){}



voidsave(constchar*pFilename);

voidload(constchar*pFilename);



// just to show how to do it



voidsetDemoValues()

{

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() 函数,它演示了如何建立一个默认设置的树的对象,保存它以及重新载入它:
`intmain(void)

{

AppSettings settings;



settings.save("appsettings2.xml");

settings.load("appsettings2.xml");

return0;

}
`
下面这个 main() 函数演示了如何建立,修改,保存,还有加载一个结构体:
`intmain(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());

}

return0;

}
`
当 save() 函数和 load() 函数完成后(在下面),运行这个 main() 函数,在控制台会显示:
HitchHikerApp: Don't Panic

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

HitchHikerApp: Thanks for all the fish

编码为 XML

有许多不同的方法来解决如何将它(一个结构体)保存在文件中。比如:
`voidAppSettings::save(constchar*pFilename)

{

TiXmlDocument doc;

TiXmlElement*msg;

TiXmlComment*comment;

strings;

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

doc.LinkEndChild(decl);



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

doc.LinkEndChild(root);



comment=newTiXmlComment();

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

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

root->LinkEndChild(comment);



// block: messages



{

MessageMap::iteratoriter;



TiXmlElement*msgs=newTiXmlElement("Messages");

root->LinkEndChild(msgs);



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

{

conststring&key=(*iter).first;

conststring&value=(*iter).second;

msg=newTiXmlElement(key.c_str());

msg->LinkEndChild(newTiXmlText(value.c_str()));

msgs->LinkEndChild(msg);

}

}



// block: windows



{

TiXmlElement*windowsNode=newTiXmlElement("Windows");

root->LinkEndChild(windowsNode);



list<WindowSettings>::iteratoriter;



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

{

constWindowSettings&w=*iter;



TiXmlElement*window;

window=newTiXmlElement("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=newTiXmlElement("Connection");

root->LinkEndChild(cxn);

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

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

}



doc.SaveFile(pFilename);

}
`
运行这个修改过的 main() 函数会生成如下的文件:
`<?xmlversion="1.0"?>

<HitchHikerApp>

<!-- Settings for HitchHikerApp -->

<Messages>

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

<Welcome>Don&apos;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 解码

就像给一个对象编码一样,也有许多办法可以把 XML 解码为你自己的 C++ 对象结构。下面的方法使用了 TiXmlHandles 类。
`voidAppSettings::load(constchar*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())

{

constchar*pKey=pElem->Value();

constchar*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;

constchar*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 文件,然后使用上面所说的递归遍历的方式将 XML 结构输出到标准输出中。
`// tutorial demo program



#include"stdafx.h"

#include"tinyxml.h"
`

// ----------------------------------------------------------------------



// STDOUT dump and indenting utility functions



// ----------------------------------------------------------------------



constunsignedintNUM_INDENTS_PER_SPACE=2;

constchar*getIndent(unsignedintnumIndents)

{

staticconstchar*pINDENT=" + ";

staticconstunsignedintLENGTH=strlen(pINDENT);

unsignedintn=numIndents*NUM_INDENTS_PER_SPACE;

if(n>LENGTH)n=LENGTH;

return&pINDENT[LENGTH-n];

}

// same as getIndent but no "+" at the end



constchar*getIndentAlt(unsignedintnumIndents)

{

staticconstchar*pINDENT=" ";

staticconstunsignedintLENGTH=strlen(pINDENT);

unsignedintn=numIndents*NUM_INDENTS_PER_SPACE;

if(n>LENGTH)n=LENGTH;

return&pINDENT[LENGTH-n];

}

intdump_attribs_to_stdout(TiXmlElement*pElement,unsignedintindent)

{

if(!pElement)return0;

TiXmlAttribute*pAttrib=pElement->FirstAttribute();

inti=0;

intival;

doubledval;

constchar*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();

}

returni;

}

voiddump_to_stdout(TiXmlNode*pParent,unsignedintindent=0)

{

if(!pParent)return;

TiXmlNode*pChild;

TiXmlText*pText;

intt=pParent->Type();

printf("%s",getIndent(indent));

intnum;

switch(t)

{

caseTiXmlNode::DOCUMENT:

printf("Document");

break;

caseTiXmlNode::ELEMENT:

printf("Element [%s]",pParent->Value());

num=dump_attribs_to_stdout(pParent->ToElement(),indent+1);

switch(num)

{

case0:printf(" (No attributes)");break;

case1:printf("%s1 attribute",getIndentAlt(indent));break;

default:printf("%s%d attributes",getIndentAlt(indent),num);break;

}

break;

caseTiXmlNode::COMMENT:

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

break;

caseTiXmlNode::UNKNOWN:

printf("Unknown");

break;

caseTiXmlNode::TEXT:

pText=pParent->ToText();

printf("Text: [%s]",pText->Value());

break;

caseTiXmlNode::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



voiddump_to_stdout(constchar*pFilename)

{

TiXmlDocument doc(pFilename);

boolloadOkay=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



// ----------------------------------------------------------------------



intmain(intargc,char*argv[])

{

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

{

dump_to_stdout(argv[i]);

}

return0;

}

在命令行或 DOS 窗口中这样运行它,比如:
`C:\dev\tinyxml>Debug\tinyxml_1.exe example1.xml



example1.xml:

Document

+Declaration

+Element[Hello]

(No attributes)

+Text:[World]
`
作者以及变动
  • ```
    由 Ellers 于2005年4月~6月编写
* ```由 Lee Thomason 于2005年9月整理入 doc 文档中
  • ```
    2005年10月由 Ellers 进行更新

因翻译仓促,水平有限,文中如有疏漏或错误,请不吝赐教。联系作者:


Email / MSN: gao.hongda@gmail.com


blog: http://LoveRene.cublog.cn/
```
原文链接: https://www.cnblogs.com/kex1n/archive/2010/10/03/1841502.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/15716

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午3:45
下一篇 2023年2月7日 下午3:47

相关推荐