Linux下C++解析xml文档
因为工作的需要,需要在项目中使用到xml来存储一些特定的配置参数,因此了解了一下在Linux下如何使用C++解析xml文档,以下使用到的netConfig.xml文档内容见附录netConfig.xml,文章中用到的是Linux自带的libxml2来解析xml。
首先先来看一下几个常用的函数
//获取xml文件指针,返回的xml文件指针 <parse.h>
xmlDocPtr xmlReadFile(const char *URL, const char *encoding, int options);
//同上 <parse.h>
xmlDocPtr xmlParseFile(const char *filename);
//字符串转换为xml文档 <parse.h>
xmlDocPtr xmlParseMemory(const char *buffer,int size);
//获取xml文档根节点 <tree.h>
xmlNodePtr xmlDocGetRootElement(const xmlDoc *doc);
//比较xml字符串,与strcmp功能类似 <xmlstring.h>
int xmlStrcmp(const xmlChar *str1,const xmlChar *str2);
//指向当前节点的第一个子节点
curr = curr ->xmlChildrenNode;
//指向当前节点的下一个兄弟(平行)节点
curr = curr -> next;
//获取xml节点的内容 <tree.h>
xmlChar xmlNodeGetContent(const xmlNode *cur);
//释放xml文档 <tree.h>
void xmlFreeDoc(xmlDocPtr cur);
//忽略xml文档中的空格、回车等 <parse.h>
int xmlKeepBlanksDefault(int val);
typedef struct _xmlNode xmlNode;
typedef xmlNode *xmlNodePtr;
struct _xmlNode {
void *_private;/* application data */
xmlElementType type; /* type number, must be second ! */
const xmlChar *name; /* the name of the node, or the entity */
struct _xmlNode *children; /* parent->childs link */
struct _xmlNode *last; /* last child link */
struct _xmlNode *parent;/* child->parent link */
struct _xmlNode *next; /* next sibling link */
struct _xmlNode *prev; /* previous sibling link */
struct _xmlDoc *doc;/* the containing document */
/* End of common part */
xmlNs *ns; /* pointer to the associated namespace */
xmlChar *content; /* the content */
struct _xmlAttr *properties;/* properties list */
xmlNs *nsDef; /* namespace definitions on this node */
void *psvi;/* for type/PSVI informations */
unsigned short line; /* line number */
unsigned short extra; /* extra data for XPath/XSLT */
};
一、装载xml文档
解析xml文档前,需要打开装载xml文档,这里可以使用xmlReadFile或者xmlParseFile都可以打开,先看一下程序如何实现
//例程1.1:使用xmlParseFile装载xml文件
char* xml_PATH = (char *)"netConfig.xml"; //指定xml文档存放位置
xmlDocPtr pDoc = xmlParseFile(xml_PATH); //使用xmlParFile打开xml文档
if(null == pDoc) //若xml文档指针为空,则表示没有xml文档或者打开失败
{
printf("xml file load failed!\n");
return -1;
}
//do somethings
xmlFreeDoc(pDoc); //使用完后,需释放xml文件指针
//例程1.2:使用xmlReadFile装载xml文件
char* xml_PATH = (char *)"netConfig.xml";
xmlDocPtr pDoc = xmlReadFile(xml_PATH,"UTF-8",XML_PARSE_RECOVER); //文档编码格式采用UTF-8,出错时恢复
if(null == pDoc) //若xml文档指针为空,则表示没有xml文档或者打开失败
{
printf("xml file load failed!\n");
return -1;
}
//do somethings
xmlFreeDoc(pDoc); //使用完后,需释放xml文件指针
这里需要注意的是,两种打开方式返回的都是xmlDocPtr类型,唯独不同的是参数,我们看一下这两个函数的原型
xmlDocPtr xmlParseFile(const char *filename);
xmlDocPtr xmlReadFile(const char *URL, const char *encoding, int options);
使用xmlParseFile时,只需要传入文档路径即可,打开成功就会返回xmlDocPtr的文件指针。而xmlReadFile则除了需要传入文档路径外,还需要指定文档编码格式,这里我用utf-8的编码格式打开,当文档编码格式为非UTF-8的格式时,会自动转换为指定的编码格式。
另外,xmlReadFile还可以打开网络链接url地址的xml文档,因此在功能上来说个人觉得xmlReadFile比xmlParseFile更为完善。
二、解析xml文档
2.1 获取xml文档的根节点
在根据xml文档的编写规则,每个xml文档有且只有一个根结点,tinyxml2给出了xml获取根节点的函数
//获取xml根结点函数原型
xmlNodePtr xmlDocGetRootElement(const xmlDoc *doc);
//例程2.1 获取xml根结点,接上节程序
xmlNodePtr pRoot = xmlDocGetRootElement(pDoc); //获取根节点
if(null == pRoot)
{
cout << "get xml Node failed"<<endl;
xmlFreeDoc(pDoc); //若获取根节点失败,则释放xml文件
return 0;
}
printf("Node name is \"%s\"\n",pRoot->name);
2.2 获取xml根节点的子节点
xml中,在根节点会嵌套包含子节点,在读取完根节点后,需要获取根节点的子节点及其属性和值,那么我们就需要用到 "->"操作符和xmlNodeGetContent()来获取子节点和子节点的值。
//例程2.2 获取根节点的子节点montitor
xmlChar * value = null;
xmlNodePtr pFirst = pRoot->xmlChildrenNode; //从根节点往下查找子节点
if(null == pFirst)
{
printf("%s No children Node!\n",pRoot->name);
xmlFreeDoc(pDoc); //若无子节点,则释放xml文件
return -2;
}
printf("The %s Node's children node name is \"%s\"\n",pRoot->name,pFirst -> name);
//获取节点的值
xmlNodePtr pNode = pFirst -> children; //获取montitor的子节点
if(null == pNode) //若返回值为空,则说明该节点无子节点
{
printf("%s No more children node !\n",pFirst -> name);
}
else
{
value = xmlNodeGetContent(pNode); //获取子节点的值(内容)
printf("The \"%s\" Node' childrenNode name is \"%s\" --> \"%s\"\n",pFirst -> name,pNode->name,value);
}
2.3 遍历子节点
附录中的netConfig.xml文档中,montitor节点下存在多个子节点,这些子节点相互之间的关系为共存,我们称之为兄弟节点(个人喜欢称之为平行节点)。
xmlNodePtr childrenNode = pNode -> children ; //获取pNode的子节点
while(childrenNode != NULL) //当存在子节点时
{
if(!xmlStrcmp(childrenNode->name,BAD_CAST"comment")) //判断子节点标签是否为comment
{
childrenNode = childrenNode->next;
}
value = xmlNodeGetContent(childrenNode); //获取子节点的内从
printf("The Node name is \"%s\" --> \"%s\"\n",childrenNode -> name,value);
childrenNode = childrenNode->next; //children指向下一个平行节点
}
三、完整C++程序
3.1 C++解析源码
这里贴出来完整的程序,各位有兴趣可以尝试编译运行一下。
//例程3.1 C++解析xml文件完整程序
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <iostream>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
using namespace std;
char *xml_PATH = (char *)"netConfig.xml";
int main(int agrc,char *argv[])
{
xmlChar * value = NULL;
xmlKeepBlanksDefault(0); //忽略空格、回车
xmlDocPtr pDoc = xmlReadFile(xml_PATH,NULL,XML_PARSE_RECOVER); //获取xml文件指针
if(pDoc == NULL)
{
cout<<"xml open failed"<<endl; //若文件指针为空,则报告文件打开失败
return 0;
}
xmlNodePtr pRoot = xmlDocGetRootElement(pDoc); //获取根节点
if(NULL == pRoot)
{
cout << "get xml Node failed"<<endl;
xmlFreeDoc(pDoc); //若获取根节点失败,则释放xml文件指针
return 0;
}
printf("Node name is \"%s\"\n",pRoot->name);
xmlNodePtr pFirst = pRoot->xmlChildrenNode; //从根节点往下查找子节点
if(NULL == pFirst)
{
printf("%s No children Node!\n",pRoot->name);
xmlFreeDoc(pDoc); //若无子节点,则释放xml文件指针
return 0;
}
printf("The %s Node‘ children node name is \"%s\"\n",pRoot->name,pFirst -> name);
xmlNodePtr pSecond = pFirst->children;
if (NULL == pSecond)
{
printf("%s No children Node!\n",pSecond -> name);
xmlFreeDoc(pDoc); //若无子节点,则释放xml文件指针
return 0;
}
value = xmlNodeGetContent(pSecond);
printf("The Second Node name is \"%s\" --> \"%s\"\n",pSecond -> name,value);
xmlNodePtr pThird = pSecond->next;
if (NULL == pThird)
{
printf("%s No children Node!\n",pThird -> name);
xmlFreeDoc(pDoc); //若无子节点,则释放xml文件指针
return 0;
}
while(pThird != NULL)
{
if(!xmlStrcmp(pThird->name,BAD_CAST"comment"))
{
pThird = pThird->next;
}
value = xmlNodeGetContent(pThird);
printf("The Node name is \"%s\" --> \"%s\"\n",pThird -> name,value);
pThird = pThird->next;
}
if(NULL == pThird)
printf("No more node!\n");
xmlFreeDoc(pDoc);
return 0;
}
3.2 编译运行
g++ main.cpp -o main -I /usr/include/libxml2 -L /usr/local/lib -lxml2
解析结果:
#!/bin/sh
yangwang@yangwang:~/xml/xmlDome$ ./main
Node name is "root"
The root Node‘ children node name is "montitor"
The Second Node name is "name" --> "ftp"
The Node name is "path" --> "/usr/bin/"
The Node name is "interval" --> "30"
The Node name is "restartwait" --> "30"
The Node name is "ip" --> "127.0.0.1"
The Node name is "port" --> "9878"
The Node name is "request" --> "1;monitor"
The Node name is "response" --> "The ftp is working ok."
The Node name is "log" --> "log/2"
No more node!
附录、netConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<montitor>
<name>ftp</name> <!--监控的程序名称-->
<path>/usr/bin/</path> <!--程序绝对路径-->
<interval>30</interval> <!--间隔多久监控一次-->
<restartwait>30</restartwait> <!--程序被重启后,等待多久再进行监控-->
<ip>127.0.0.1</ip> <!--程序IP-->
<port>9878</port> <!--程序端口-->
<request>1;monitor</request> <!--发送给程序的报文-->
<response>The ftp is working ok.</response> <!--程序给监控的反馈报文-->
<log>
<folderpath>log/</folderpath> <!--程序日志保存目录-->
<savedays>2</savedays> <!--程序日志保存时间-->
</log>
</montitor>
</root>