微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

xml解析器tinyXML

转自:http://blog.163.com/kin_jiezi/blog/static/3683682201172593616285/

读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好。

TinyXML是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。

DOM模型即文档对象模型,是将整个文档分成多个元素(如书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系。

如下是一个XML片段:
< Persons >
Person ID ="1" name >周星星 </ age >20 Person ="2" >白晶晶 >18 >

在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)导入工程就可以用它的东西了。如果需要,可以将它做成自己的DLL来调用。举个例子就可以说明一切。。。

对应的XML文件
>phinecos >22 >

读写XML文件的程序代码

#include<iostream>
#include " tinyxml.h "
#include tinystr.h "
#include< string>
#include<windows.h>
#include<atlstr.h>
using namespacestd;

CStringGetAppPath()
{//获取应用程序根目录
TCHARmodulePath[MAX_PATH];
GetmodulefileName(NULL,modulePath,MAX_PATH);
CStringstrModulePath(modulePath);
strModulePath=strModulePath.Left(strModulePath.ReverseFind(_T('\\')));
returnstrModulePath;
}


boolCreateXmlFile( string&szFileName)
{创建XML文件,szFilePath为文件保存的路径,若创建成功返回true,否则false
try
{
创建一个XML的文档对象。
tixmlDocument*myDocument=newtixmlDocument();
创建一个根元素并连接。
tixmlElement*RootElement=newtixmlElement("Persons");
myDocument->LinkEndChild(RootElement);
创建一个Person元素并连接。
tixmlElement*PersonElement=Person");
RootElement->LinkEndChild(PersonElement);
设置Person元素的属性
PersonElement->SetAttribute(ID",1");
创建name元素、age元素并连接。
tixmlElement*NameElement=name");
tixmlElement*AgeElement=age");
PersonElement->LinkEndChild(NameElement);
PersonElement->LinkEndChild(AgeElement);
设置name元素和age元素的内容并连接。
tixmlText*NameContent=newtixmlText(周星星");
tixmlText*AgeContent=22");
NameElement->LinkEndChild(NameContent);
AgeElement->LinkEndChild(AgeContent);
CStringappPath=GetAppPath();
stringseperator=";
stringfullPath=appPath.GetBuffer(0)+seperator+szFileName;
myDocument->SaveFile(fullPath.c_str());保存到文件
}

catch(string&e)
{
returnfalse;
}

true;
}


boolreadxmlFile( string&szFileName)
{读取Xml文件,并遍历try
{
CStringappPath=GetAppPath();
0)+seperator+szFileName;
newtixmlDocument(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;
}

string&e)
{
true;
}

intmain()
{
stringfileName=info.xml";
CreateXmlFile(fileName);
readxmlFile(fileName);
}

1.首先下载TinyXML库的文件,这里给出链接,大家自己去下吧
http://prdownloads.sourceforge.net/tinyxml

2.在TinyXML的目录里面找到tinystr.h,tinyxml.h,tinystr.cpp,tinyxml.cpp,tinyxmlerror.cpp,tinyxmlparser.cpp六个文件加入到刚刚创建的项目中去

3.在相应的工程文件中加入两个头文件
#include "tinyxml.h"
#include "tinystr.h"

4.tinystr.cpp,tinyxmlparser.cpp六个文件的第一行加入头文件
#include "stdafx.h"
至于为什么,在vc的win32工程cpp文件中,如果不加该文件头的话会提示文件没有结束标志

在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的一些操作。

例如:

<? xmlversion="1.0"standalone=no>
<!–Ourtodolistdata–>
<Todo>
<Itempriority="1">Gotothe<bold>Toystore!</bold></Item>
<Itempriority="2">dobills</Item>
</Todo>



整个对象树:

tixmlDocument "demo.xml"
tixmlDeclaration "version=’1.0′" "standalone=no"
tixmlComment " Our to do list data"
tixmlElement "Todo"
tixmlElement "Item" Attribtutes: priority = 1
tixmlText "Go to the "
tixmlElement "bold"
tixmlText "Toy store!"
tixmlElement "Item" Attributes: priority=2
tixmlText "Do bills"

tinyXML中,用FirstChild"名字"时,FirstChild函数点与要找的点必父子

句柄

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

tixmlElement * root = document.FirstChildElement( " Document );
if (root)
{
tixmlElement
*element=root->FirstChildElement("Element);
if(element)
childChild(child)
child2NextSiblingElement((child2)
{
Finallydosomethinguseful.



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

tixmlHandledocHandle( & document);
tixmlElement
child2 docHandle.FirstChild( ).FirstChild( Element ).Child( Child , 1 ).toElement();
(child2)
dosomethinguseful

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

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

要设置broADCAST_VERSION节点的值 8为其他值,可参考如下代码(将值加1):
用ReplaceChild( tixmlNode* replaceThis,const tixmlNode& withThis )方法替换
tixmlDocumentdoc("zapp.conf");
doc.LoadFile();
tixmlHandledocHandle( doc);
tixmlElement
broadcast_ver ).FirstChildElement( ).toElement();
tixmlNode
oldnode -> FirstChild();
const char ver GetText();
int oldVer atoi(ver);
CStringnewVer;
newVer.Format(
%d + );
tixmlTextnewText(newVer);
broadcast_ver
ReplaceChild(oldnode,newText);
AfxMessageBox(broadcast_ver
GetText()); // 输出 doc.SaveFile();

二,删除节点,属性
RemoveChild( tixmlNode* removeThis )方法删除父节点的子节点,
RemoveAttribute( const char * name )方法删除属性值.
例如删除broADCAST_VERSION节点
).toElement();

tixmlNode node FirstChild( );

broadcast_ver
RemoveChild(node);

也可以删除整个SOURCE_1节点:
);

broadcast 删除broADCAST_VERSION的info属性:
).toElement();

broadcast_ver RemoveAttribute( ); 删除info

可以借助NextSiblingElement()方法实现递归删除.
三,添加节点,51); font-family:Arial; font-size:14px; line-height:26px">例如在SOURCE_3下添加broADCAST_PID节点:
).toElement();
tixmlElement broadcast_Pid new tixmlElement( broADCAST_PID );
tixmlText
text tixmlText( 7215 );
broadcast_Pid
SetAttribute( thepid LinkEndChild(text);
broadcast
LinkEndChild(broadcast_Pid);

将在SOURCE_3后添加新的节点:
broADCAST_PID ="thepid" 四,最后说一下中文乱码的问题

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

void ConvertUtf8ToGBK(CString strUtf8)
intlenMultiBytetoWideChar(CP_UTF8,0-1);
unsigned
shortwszGBKnewunsigned[len+];
memset(wszGBK,len
2);
MultiBytetoWideChar(CP_UTF8,wszGBK,len);

len
WideCharToMultiByte(CP_ACP,NULL);
charszGBK];
memset(szGBK,0)">);
WideCharToMultiByte(CP_ACP,szGBK,len,NULL);

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



ConvertGBKToUtf8(CString strGBK)
MultiBytetoWideChar(CP_ACP,(LPCTSTR)strGBK,0)">wszUtf8];
memset(wszUtf8,0)">);
MultiBytetoWideChar(CP_ACP,wszUtf8,0)">WideCharToMultiByte(CP_UTF8,0)">szUtf8
];
memset(szUtf8,0)">);
WideCharToMultiByte(CP_UTF8,szUtf8,NULL);

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



现在越来越多的数据和配置采用了xml格式来存放和进行传输解析了。在c++方面,没有本地支持的库,所以需要我们自己去找一下。微软的msxml说实话,确实不咋地,尤其是com的类型变量名字一直指针,让众人看上去就比较反感。开源的tinyxml在这方便做的还不错。简单介绍下使用过程的一点小经验。

在这里发下牢骚,VC6.0以后的各个版本的VS环境对于C++的智能感知都是那么的SB,不管你怎么配置,怎么google都让你非常抓狂,就是不出来。算了不说了。


从网站上下载tinyxml,下载之后解压打开文件夹,里面有一些测试例子,tinyxml.sln支持vs2010了都,不管这些,怎么需要的是那个xml类库。

使用tinyxml我们只需要

tinyxml.cpp,

tinyxml.h,51); font-family:Arial; font-size:14px; line-height:26px"> tinyxmlerror.cpp,51); font-family:Arial; font-size:14px; line-height:26px"> tinyxmlparser.cpp,51); font-family:Arial; font-size:14px; line-height:26px"> tinystr.cpp,153)">tinystr.h

6个文件即可。注意一旦少拷贝了其中tinyxmlerror.cpp,tinyxmlparser.cpp,其中一个或者两个,就会报告link错误,呵呵

在目标源文件的头部,添加 #include"tinyxml.h"和#include "tinystr.h"

我们使用xml文件,无外乎这几种操作,

1. 遍历整个xml返回一棵树select

2. 查找特定节点的属性/文本值select

3.插入特定位置一个节点insert

4.更新特定节点的属性/文本值 update

5.创建XML文件 create

6.who kNows

据一位网友的博文里面提到,基本应该涵盖数据库的所有操作,xml操作应该像操作数据库一样。我觉得甚是有道理啊。

那么我来列举下,我搜集以及测试成功的相关的操作代码吧,希望能节省一些大家学习的时间。

1.create xml操作

  1. //定义一个tixmlDocument类指针
  2. tixmlDocument*pDoc=newtixmlDocument;
  3. if(NULL==pDoc)
  4. {
  5. returnfalse;
  6. }
  7. //定义一个xml文件头部声明
  8. tixmlDeclaration*pDeclaration=newtixmlDeclaration(("1.0"),(""),(""));
  9. if(NULL==pDeclaration)
  10. {
  11. false;
  12. }
  13. pDoc->LinkEndChild(pDeclaration);
  14. //生成一个根节点:MyApp
  15. tixmlElement*pRootEle=newtixmlElement(("MyApp"));
  16. if(NULL==pRootEle)
  17. pDoc->LinkEndChild(pRootEle);
  18. //生成子节点:Messages
  19. tixmlElement*pMsg=newtixmlElement(("Messages"));
  20. if(NULL==pMsg)
  21. pRootEle->LinkEndChild(pMsg);
  22. //生成子节点:Welcome
  23. tixmlElement*pWelcome=newtixmlElement(("Welcome"));
  24. if(NULL==pWelcome)
  25. pMsg->LinkEndChild(pWelcome);
  26. //设置Welcome节点的值
  27. constchar*strValue=("WelcometoMyApp");
  28. tixmlText*pWelcomeValue=newtixmlText(strValue);
  29. pWelcome->LinkEndChild(pWelcomeValue);
  30. //生成子节点:farewell
  31. tixmlElement*pfarewell=newtixmlElement(("farewell"));
  32. if(NULL==pfarewell)
  33. pMsg->LinkEndChild(pfarewell);
  34. //设置farewell节点的值
  35. strValue=("ThankyouforusingMyApp");
  36. tixmlText*pfarewellValue=newtixmlText(strValue);
  37. pfarewell->LinkEndChild(pfarewellValue);
  38. //生成子节点:Windows
  39. tixmlElement*pWindows=newtixmlElement(("Windows"));
  40. if(NULL==pWindows)
  41. pRootEle->LinkEndChild(pWindows);
  42. //生成子节点:Window
  43. tixmlElement*pWindow=newtixmlElement(("Window"));
  44. if(NULL==pWindow)
  45. pWindows->LinkEndChild(pWindow);
  46. //设置节点Window的值
  47. pWindow->SetAttribute(("name"),("MainFrame"));
  48. pWindow->SetAttribute(("x"),("5"));
  49. pWindow->SetAttribute(("y"),("15"));
  50. pWindow->SetAttribute(("w"),("400"));
  51. pWindow->SetAttribute(("h"),("250"));
  52. //生成子节点:Window
  53. tixmlElement*pConnection=newtixmlElement(("Connection"));
  54. if(NULL==pConnection)
  55. pRootEle->LinkEndChild(pConnection);
  56. //设置节点Connection的值
  57. pConnection->SetAttribute(("ip"),("192.168.0.1"));
  58. pConnection->SetAttribute(("timeout"),("123.456000"));
  59. pDoc->SaveFile("1.xml");

2.遍历打印xml文件 select操作

copy
    //tixmlDocument*pDoc=newtixmlDocument();
  1. if(NULL==pDoc)
  2. pDoc->LoadFile("1.xml");
  3. pDoc->Print();

3.获取单个节点值

copy

    boolGetNodePointerByName(tixmlElement*pRootEle,std::string&strNodeName,tixmlElement*&Node)
  1. //假如等于根节点名,就退出
  2. if(strNodeName==pRootEle->Value())
  3. Node=pRootEle;
  4. true;
  5. tixmlElement*pEle=pRootEle;
  6. for(pEle=pRootEle->FirstChildElement();pEle;pEle=pEle->NextSiblingElement())
  7. //递归处理子节点,获取节点指针
  8. if(GetNodePointerByName(pEle,strNodeName,Node))
  9. true;
  10. }

copy
    boolQueryNode_Text(std::stringXmlFile,std::stringstrNodeName,std::string&strText)
  1. newtixmlDocument();
  2. pDoc->LoadFile(XmlFile);
  3. tixmlElement*pRootEle=pDoc->RootElement();
  4. tixmlElement*pNode=NULL;
  5. GetNodePointerByName(pRootEle,pNode);
  6. if(NULL!=pNode)
  7. strText=pNode->GetText();
  8. else
  9. copy
      boolQueryNode_Attribute(std::stringXmlFile,std::map<std::string,std::string>&AttMap)
    1. typedefstd::pair<std::string,std::string>String_Pair;
    2. tixmlDocument*pDoc=newtixmlDocument();
    3. pDoc->LoadFile(XmlFile);
    4. tixmlElement*pRootEle=pDoc->RootElement();
    5. if(NULL==pRootEle)
    6. tixmlElement*pNode=NULL;
    7. GetNodePointerByName(pRootEle,pNode);
    8. if(NULL!=pNode)
    9. tixmlAttribute*pAttr=NULL;
    10. for(pAttr=pNode->FirstAttribute();pAttr;pAttr=pAttr->Next())
    11. std::stringstrAttName=pAttr->Name();
    12. std::stringstrAttValue=pAttr->Value();
    13. AttMap.insert(String_Pair(strAttName,strAttValue));
    14. else
    15. }

    4.删除节点操作

    copy

      boolDelNode(std::stringXmlFile,std::stringstrNodeName)
    1. //假如是根节点
    2. if(pRootEle==pNode)
    3. if(pDoc->RemoveChild(pRootEle))
    4. pDoc->SaveFile(XmlFile);
    5. //假如是其它节点
    6. tixmlNode*pParNode=pNode->Parent();
    7. if(NULL==pParNode)
    8. tixmlElement*pParentEle=pParNode->toElement();
    9. if(NULL!=pParentEle)
    10. if(pParentEle->RemoveChild(pNode))
    11. pDoc->SaveFile(XmlFile);
    12. 5.修改节点操作

      copy

        boolModifyNode_Text(std::stringXmlFile,std::stringstrText)
      1. pNode->Clear();//首先清除所有文本
      2. //然后插入文本,保存文件
      3. tixmlText*pValue=newtixmlText(strText);
      4. pNode->LinkEndChild(pValue);
      5. copy
          boolModifyNode_Attribute(std::stringXmlFile,
        1. std::map<std::string,std::string>&AttMap)
        2. std::stringstrAttName=_T("");
        3. std::stringstrAttValue=_T("");
        4. strAttName=pAttr->Name();
        5. teratoriter;
        6. for(iter=AttMap.begin();iter!=AttMap.end();iter++)
        7. if(strAttName==iter->first)
        8. pAttr->SetValue(iter->second);
        9. 6增加节点操作

          copy

            boolAddNode_Text(std::stringXmlFile,std::stringstrParNodeName,strParNodeName,0); background-color:inherit">//生成子节点:pNewNode
          1. tixmlElement*pNewNode=newtixmlElement(strNodeName);
          2. if(NULL==pNewNode)
          3. //设置节点文本,然后插入节点
          4. tixmlText*pNewValue=newtixmlText(strText);
          5. pNewNode->LinkEndChild(pNewValue);
          6. pNode->InsertEndChild(*pNewNode);
          7. copy
              boolAddNode_Attribute(std::stringXmlFile,0); background-color:inherit">//设置节点的属性值,然后插入节点
            1. pNewNode->SetAttribute(iter->first,iter->second);
            2. pNode->InsertEndChild(*pNewNode);
            3. }

            版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。