Linux下C++解析xml文档

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> 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放学校门口见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值