优秀的C++ Xml解析器TinyXml

转载:http://www.cnblogs.com/phinecos/archive/2008/03/11/1100912.html
`

http://www.cnblogs.com/ggjucheng/archive/2012/01/03/2311426.html
http://www.cppblog.com/elva/archive/2010/01/17/47907.html

一、前言
TinyXML是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译,使用TinyXML进行C++ XML解析,使用简单,容易上手。
这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。
DOM模型即文档对象模型,是将整个文档分成多个元素(如书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系。

二、TinyXML介绍
在TinyXML中,根据XML的各种元素来定义了一些类:
TiXmlBase:整个TinyXML模型的基类。
TiXmlAttribute:对应于XML中的元素的属性。
TiXmlNode:对应于DOM结构中的节点。
TiXmlComment:对应于XML中的注释
TiXmlDeclaration:对应于XML中的申明部分,即<?versiong=”1.0” ?>。
TiXmlDocument:对应于XML的整个文档。
TiXmlElement:对应于XML的元素。
TiXmlText:对应于XML的文字部分
TiXmlUnknown:对应于XML的未知部分。
TiXmlHandler:定义了针对XML的一些操作。

TinyXML是个解析库,主要由DOM模型类(TiXmlBase、TiXmlNode、TiXmlAttribute、TiXmlComment、TiXmlDeclaration、TiXmlElement、TiXmlText、TiXmlUnknown)和操作类(TiXmlHandler)构成。它由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,用的时候,只要将(tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp)导入工程就可以用它的东西了

三、下载和编译
本文运行环境是: Redhat 5.5 + g++version 4.6.1 + GNU Make 3.81 + tinyxml_2_6_2

下载地址是:http://sourceforge.net/projects/tinyxml/

解压之后得到文件夹tinyxml,tinyxml的头文件和cpp文件,都在这个文件夹,为了我们的管理我们的项目工程,我们还是把tinyxml做一个整理。

由于tinyxml不仅支持Linux编译,同时也支持windows下编译,所以解压之后不仅有h文件,cpp文件,还有vc工程的一些工程文件,这里我们只在Linux上使用,所以只留下h文件和cpp文件,其他文件一概删除

四、例子一:

<Persons>
    <Person ID="1">
        <name>phinecos</name>
        <age>22</age>
    </Person>
</Persons>
#include <iostream>
#include "tinyxml.h"
#include "tinystr.h"
#include <string>
#include <windows.h>
#include <atlstr.h>
using namespace std;

CString GetAppPath()
{//获取应用程序根目录
    TCHAR modulePath[MAX_PATH];
    GetModuleFileName(NULL, modulePath, MAX_PATH);
    CString strModulePath(modulePath);
    strModulePath = strModulePath.Left(strModulePath.ReverseFind(_T('\\')));
    return strModulePath;
}

bool CreateXmlFile(string& szFileName)
{//创建xml文件,szFilePath为文件保存的路径,若创建成功返回true,否则false
    try
    {
        //创建一个XML的文档对象。
        TiXmlDocument *myDocument = new TiXmlDocument();
        //创建一个根元素并连接。
        TiXmlElement *RootElement = new TiXmlElement("Persons");
        myDocument->LinkEndChild(RootElement);
        //创建一个Person元素并连接。
        TiXmlElement *PersonElement = new TiXmlElement("Person");
        RootElement->LinkEndChild(PersonElement);
        //设置Person元素的属性。
        PersonElement->SetAttribute("ID", "1");
        //创建name元素、age元素并连接。
        TiXmlElement *NameElement = new TiXmlElement("name");
        TiXmlElement *AgeElement = new TiXmlElement("age");
        PersonElement->LinkEndChild(NameElement);
        PersonElement->LinkEndChild(AgeElement);
        //设置name元素和age元素的内容并连接。
        TiXmlText *NameContent = new TiXmlText("周星星");
        TiXmlText *AgeContent = new TiXmlText("22");
        NameElement->LinkEndChild(NameContent);
        AgeElement->LinkEndChild(AgeContent);
        CString appPath = GetAppPath();
        string seperator = "\\";
        string fullPath = appPath.GetBuffer(0) +seperator+szFileName;
        myDocument->SaveFile(fullPath.c_str());//保存到文件
    }
    catch (string& e)
    {
        return false;
    }
    return true;
}

bool ReadXmlFile(string& szFileName)
{//读取Xml文件,并遍历
    try
    {
        CString appPath = GetAppPath();
        string seperator = "\\";
        string fullPath = appPath.GetBuffer(0) +seperator+szFileName;
        //创建一个XML的文档对象。
        TiXmlDocument *myDocument = new TiXmlDocument(fullPath.c_str());
        myDocument->LoadFile();
        //获得根元素,即Persons。
        TiXmlElement *RootElement = myDocument->RootElement();
        //输出根元素名称,即输出Persons。
        cout << RootElement->Value() << endl;
        //获得第一个Person节点。
        TiXmlElement *FirstPerson = RootElement->FirstChildElement();
        //获得第一个Person的name节点和age节点和ID属性。
        TiXmlElement *NameElement = FirstPerson->FirstChildElement();
        TiXmlElement *AgeElement = NameElement->NextSiblingElement();
        TiXmlAttribute *IDAttribute = FirstPerson->FirstAttribute();
        //输出第一个Person的name内容,即周星星;age内容,即;ID属性,即。
        cout << NameElement->FirstChild()->Value() << endl;
        cout << AgeElement->FirstChild()->Value() << endl;
        cout << IDAttribute->Value()<< endl;
    }
    catch (string& e)
    {
        return false;
    }
    return true;
}
int main()
{
    string fileName = "info.xml";
    CreateXmlFile(fileName);
    ReadXmlFile(fileName);
}

五、例子二:
我们在conf目录下建立student.xml,xml代码如下:

<School name="软件学院">  
    <Class name = "C++">  
        <Student name="tinyxml" number="123">  
            <email>tinyxml@163.com</email>  
            <address>中国</address>           
        </Student>  
        <Student name="jsoncpp" number="456">  
            <email>jsoncpp@gmail.com</email>  
            <address>美国</address>           
        </Student>  
    </Class>  
</School>

使用tinyxml,我们只需要在头文件中包含 <tinyxml.h>就行了。
打印整个XML代码如下:

void printSchoolXml() {
    using namespace std;
    TiXmlDocument doc;  
    const char * xmlFile = "conf/school.xml";   
    if (doc.LoadFile(xmlFile)) {    
        doc.Print();  
    } else {
        cout << "can not parse xml conf/school.xml" << endl;
    }   
}
void readSchoolXml() {
    using namespace std;
    const char * xmlFile = "conf/school.xml";
    TiXmlDocument doc;                              
    if (doc.LoadFile(xmlFile)) {
        doc.Print();
    } else {
        cout << "can not parse xml conf/school.xml" << endl;
        return;
    }
    TiXmlElement* rootElement = doc.RootElement();  //School元素  
    TiXmlElement* classElement = rootElement->FirstChildElement();  // Class元素
    TiXmlElement* studentElement = classElement->FirstChildElement();  //Students  
    for (; studentElement != NULL; studentElement = studentElement->NextSiblingElement() ) {
        TiXmlAttribute* attributeOfStudent = studentElement->FirstAttribute();  //获得student的name属性  
        for (;attributeOfStudent != NULL; attributeOfStudent = attributeOfStudent->Next() ) {
            cout << attributeOfStudent->Name() << " : " << attributeOfStudent->Value() << std::endl;       
        }                                 

        TiXmlElement* studentContactElement = studentElement->FirstChildElement();//获得student的第一个联系方式 
        for (; studentContactElement != NULL; studentContactElement = studentContactElement->NextSiblingElement() ) {
            string contactType = studentContactElement->Value();
            string contactValue = studentContactElement->GetText();
            cout << contactType  << " : " << contactValue << std::endl;           
        }   

    } 
}
void writeSchoolXml() {
    using namespace std;
    const char * xmlFile = "conf/school-write.xml"; 
    TiXmlDocument doc;  
    TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", "");  
    TiXmlElement * schoolElement = new TiXmlElement( "School" );  
    TiXmlElement * classElement = new TiXmlElement( "Class" );  
    classElement->SetAttribute("name", "C++");

    TiXmlElement * stu1Element = new TiXmlElement("Student");
    stu1Element->SetAttribute("name", "tinyxml");
    stu1Element->SetAttribute("number", "123");
    TiXmlElement * stu1EmailElement = new TiXmlElement("email");
    stu1EmailElement->LinkEndChild(new TiXmlText("tinyxml@163.com") );
    TiXmlElement * stu1AddressElement = new TiXmlElement("address");
    stu1AddressElement->LinkEndChild(new TiXmlText("中国"));
    stu1Element->LinkEndChild(stu1EmailElement);
    stu1Element->LinkEndChild(stu1AddressElement);

    TiXmlElement * stu2Element = new TiXmlElement("Student");
    stu2Element->SetAttribute("name", "jsoncpp");
    stu2Element->SetAttribute("number", "456");
    TiXmlElement * stu2EmailElement = new TiXmlElement("email");
    stu2EmailElement->LinkEndChild(new TiXmlText("jsoncpp@163.com"));
    TiXmlElement * stu2AddressElement = new TiXmlElement("address");
    stu2AddressElement->LinkEndChild(new TiXmlText("美国"));
    stu2Element->LinkEndChild(stu2EmailElement);
    stu2Element->LinkEndChild(stu2AddressElement);

    classElement->LinkEndChild(stu1Element);  
    classElement->LinkEndChild(stu2Element);  
    schoolElement->LinkEndChild(classElement);  

    doc.LinkEndChild(decl);  
    doc.LinkEndChild(schoolElement); 
    doc.SaveFile(xmlFile);  
}

在tinyXML中,用FirstChild(”名字”)查找节点时,调用FirstChild函数的节点与要查找的节点必须成“父子关系”

六、句柄
想要健壮地读取一个XML文档,检查方法调用后的返回值是否为null是很重要的。一种安全的检错实现可能会产生像这样的代码:

TiXmlElement* root = document.FirstChildElement( "Document" );
if ( root )
{
    TiXmlElement* element = root->FirstChildElement( "Element" );
if ( element )
{
    TiXmlElement* child = element->FirstChildElement( "Child" );
if ( child )
{
    TiXmlElement* child2 = child->NextSiblingElement( "Child" );
if ( child2 )
{
    // Finally do something useful. 
}

用句柄的话就不会这么冗长了,使用TiXmlHandle类,前面的代码就会变成这样:

TiXmlHandle docHandle( &document );
TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
if ( child2 )
{
    // do something useful 
}

七、读取XML,设置节点文本
如下XML片段:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<ZXML>
    <ZAPP>
         <VBS_RUNTIME_PARAMS>
               <BROADCAST_VERSION info="版本">8</BROADCAST_VERSION>
               <Broadcast>
                <FileCount info="资源文件个数">69</FileCount>
                <SOURCE_1>
                    <ID info="图片编号">1</ID>
                    <Version info="图片版本">1</Version>
                    <Path info="图片路径">/mnt/share/1.bmp</Path>
                    <FileMode info="文件处理模式">0</FileMode>
                </SOURCE_1>
                <SOURCE_2>
                    <Path info="图片路径">/mnt/share/2.bmp</Path>
                    <ID info="图片编号">2</ID>
                    <Version info="图片版本">1</Version>
                    <FileMode info="文件处理模式">0</FileMode>
                </SOURCE_2>
                .
                </Broadcast>
         </VBS_RUNTIME_PARAMS>
    </ZAPP>
</ZXML>

要设置BROADCAST_VERSION节点的值 8为其他值,可参考如下代码(将值加1):
用ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )方法替换

TiXmlDocument doc("zapp.conf");
doc.LoadFile();
TiXmlHandle docHandle( &doc );
TiXmlElement* Broadcast_ver = docHandle.FirstChild("ZXML").FirstChild("ZAPP").FirstChild("VBS_RUNTIME_PARAMS").FirstChildElement("BROADCAST_VERSION").ToElement();
TiXmlNode * oldnode =  Broadcast_ver->FirstChild();
const char *ver = Broadcast_ver->GetText();
int oldVer = atoi(ver);
CString newVer;
newVer.Format("%d",oldVer+1);
TiXmlText newText(newVer);
Broadcast_ver->ReplaceChild(oldnode,newText);
AfxMessageBox(Broadcast_ver->GetText());//输出值
doc.SaveFile();

八、删除节点,属性值

RemoveChild( TiXmlNode* removeThis )方法删除父节点的子节点,
RemoveAttribute( const char * name )方法删除属性值.

例如删除BROADCAST_VERSION节点

TiXmlHandle docHandle( &doc );
TiXmlElement* Broadcast_ver = docHandle.FirstChild("ZXML").FirstChild("ZAPP").FirstChild("VBS_RUNTIME_PARAMS").ToElement();

TiXmlNode * node =  Broadcast_ver->FirstChild("BROADCAST_VERSION");

Broadcast_ver->RemoveChild(node);

也可以删除整个SOURCE_1节点:

TiXmlHandle docHandle( &doc );
TiXmlElement* Broadcast = docHandle.FirstChild("ZXML").FirstChild("ZAPP").FirstChild("VBS_RUNTIME_PARAMS").FirstChild("Broadcast").ToElement();

TiXmlNode * node =  Broadcast->FirstChild("SOURCE_1");

Broadcast->RemoveChild(node);

删除BROADCAST_VERSION的info属性:

TiXmlHandle docHandle( &doc );
TiXmlElement* Broadcast_ver = docHandle.FirstChild("ZXML").FirstChild("ZAPP").FirstChild("VBS_RUNTIME_PARAMS").FirstChildElement("BROADCAST_VERSION").ToElement();

Broadcast_ver->RemoveAttribute("info"); //删除info

可以借助NextSiblingElement()方法实现递归删除.

九、添加节点,属性值

例如在SOURCE_3下添加BROADCAST_PID节点:

TiXmlHandle docHandle( &doc );
TiXmlElement* Broadcast = docHandle.FirstChild("ZXML").FirstChild("ZAPP").FirstChild("VBS_RUNTIME_PARAMS").FirstChild("Broadcast").ToElement();
TiXmlElement* Broadcast_Pid = new TiXmlElement("BROADCAST_PID");
TiXmlText *text =new TiXmlText("7215");
Broadcast_Pid->SetAttribute("info","the pid");
Broadcast_Pid->LinkEndChild(text);
Broadcast->LinkEndChild(Broadcast_Pid);

将在SOURCE_3后添加新的节点:

<BROADCAST_PID info="the pid">7215</BROADCAST_PID>

十、最后说一下中文乱码的问题

乱码是由于GB2312与UTF8之间转换不当造成的,tinyxml在处理UTF8本身没有问题,当你打开一个UTF8的文档,可以在加载的时候指定UTF8的方式,或者文档声明处指明的编码格式,tinyxml会按照相应的编码格式加载,但很多时候当我们输出或写入中文字段时会出现乱码,无论在内存,还是打印出来的内容.这是因为我们的软件通常是GB2312编码,而读取或写入的内容是UTF8,自然就会出错.可以借助网上的两个函数来实现转换(原作者不详):

 void ConvertUtf8ToGBK(CString& strUtf8) 
 {
    int len=MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, NULL,0);
    unsigned short * wszGBK = new unsigned short[len+1];
    memset(wszGBK, 0, len * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUtf8, -1, wszGBK, len);

    len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
    char *szGBK=new char[len + 1];
    memset(szGBK, 0, len + 1);
    WideCharToMultiByte (CP_ACP, 0, wszGBK, -1, szGBK, len, NULL,NULL);

    strUtf8 = szGBK;
    delete[] szGBK;
    delete[] wszGBK;
}

void ConvertGBKToUtf8(CString& strGBK)
{
    int len=MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, NULL,0);
    unsigned short * wszUtf8 = new unsigned short[len+1];
    memset(wszUtf8, 0, len * 2 + 2);
    MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, wszUtf8, len);

    len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
    char *szUtf8=new char[len + 1];
    memset(szUtf8, 0, len + 1);
    WideCharToMultiByte (CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL,NULL);

    strGBK = szUtf8;
    delete[] szUtf8;
    delete[] wszUtf8;
}

当然,你也可以用MultiByteToWideChar,WideCharToMultiByte函数自己实现转换.以上是简单应用的几个举例,理解他们,相信你已经能写出满足自己需要的代码了.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值