TinyXml-2 Tutorial 中文翻译

本教程介绍了TinyXML的基本使用方法,包括加载和解析XML文件、构建XML文档、处理属性及注释等内容。此外还展示了如何利用TinyXML进行C++对象与XML文件间的互相转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址 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="400" 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&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>

将状态(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值