使用libxml2解析XML文件

详细教程请参考:XML 教程_w3cschool

一、XML文件格式

先简单介绍xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<!--    root: 根节点
-->
<root>
    <grade value="8">
        <class value="5">
                <student name="ZhangSan" age="15" sex="male"/>
                <student name="LiSi" age="16" sex="female"/>
        </class>
    </grade>
</root>
  • XML的第一行为声明,包含版本号和字符编码,如:<?xml version="1.0" encoding="UTF\-8"?>
  • XML的注释方法:<!-- 注释内容(无其他注释符) -->
  • <root></root>是xml的一个节点,即xmlNode。而xmlNodePtr 表示指向xmlNode的指针--xmlNode*
  • <grade value="8"> 是 root的子节点,即 xmlNodePtr->xmlChildrenNode,value为节点的属性。
  • 同理,<class value="5"> 是 grade的子节点, student是class的子节点

二、使用libxml2解析XML文件

1、代码介绍

下面介绍怎么使用libxml2.so库,解析上面的xml文件,并存到 UNIT_T 结构体中。

typedef struct {
	char acName[64];            //名字
	int age;                    //年龄
	char acSex[8];              //性别
} STUDENT_T;

#define STUDENT_NUM_MAX	2
typedef struct {
	int grade;                  //年纪
	int class;                  //班级
	int student_num;            //学生数量
	STUDENT_T stStudent[STUDENT_NUM_MAX];    //学生信息
} UNIT_T;

代码需要包含这两个头文件

#include <libxml/tree.h>  
#include <libxml/parser.h>  

 附上核心代码:

UNIT_T g_stUnit;
static int parse_student(xmlNodePtr _pstXmlNode, STUDENT_T *_pStudent);

int xml_parse(const char *_pPath)
{
	xmlDocPtr pstXmlDoc = NULL;
	xmlNodePtr pstXmlNode = NULL;

    /* 判断传参的xml文件是否存在 */
	if (!file_isexist(_pPath)) {
		DEBUG_PRINTF("error: file is not exist!!!\n");
		return -1;
	}

	xmlKeepBlanksDefault(0);    //设置是否忽略空白节点,比如空格,默认值是0

	/* 1.获取树形结构 */
	pstXmlDoc =  xmlParseFile(_pPath);
	if (pstXmlDoc == NULL) {
		DEBUG_PRINTF("error: %s open fail \n", _pPath);
		return -1;
	}

	/* 2.获取根节点 */
	pstXmlNode = xmlDocGetRootElement(pstXmlDoc);
	if (pstXmlNode == NULL) {
		DEBUG_PRINTF("error: Root node check fail");
		xmlFreeDoc(pstXmlDoc);
		return -1;
	}

	/* 3.确定根节点名是否为"root",不是则返回 */
	if(xmlStrcmp(pstXmlNode->name, (const xmlChar *)"root")){
		DEBUG_PRINTF("error: root node != root");
		xmlFreeDoc(pstXmlDoc);
		return -1;
	}

	xmlChar *pTemp = NULL;
	pstXmlNode = pstXmlNode->xmlChildrenNode;						//获取子节点

	/* 4.读取"grade"节点 */
	if (!xmlStrcmp(pstXmlNode->name, (const xmlChar *)"grade")){
		pTemp = xmlGetProp(pstXmlNode, "value");                    //获取value属性
		if (pTemp != NULL) {
			g_stUnit.grade = atoi((const char *)pTemp);
			xmlFree(pTemp);
		}
	}

	/* 5.读取"grade"节点 */
	pstXmlNode = pstXmlNode->xmlChildrenNode;						//获取子节点
	if (!xmlStrcmp(pstXmlNode->name, (const xmlChar *)"class")) {
		pTemp = xmlGetProp(pstXmlNode, "value");                    //获取value属性
		if (pTemp != NULL) {
			g_stUnit.class = atoi((const char *)pTemp);
			xmlFree(pTemp);
		}
	}
	
	pstXmlNode = pstXmlNode->xmlChildrenNode;						//获取子节点
	/* 6.遍历并解析 */
	g_stUnit.student_num = 0;
	while(pstXmlNode != NULL) {
		if (!xmlStrcmp(pstXmlNode->name, (const xmlChar *)"student")) {
			parse_student(pstXmlNode, &g_stUnit.stStudent[g_stUnit.student_num++]);
		}

		pstXmlNode = pstXmlNode->next;					/* 下一个子节点(同级) */
	}

    xmlFreeDoc(pstXmlDoc);             /* 释放文档树 */

	return 0;
}

static int parse_student(xmlNodePtr _pstXmlNode, STUDENT_T *_pStudent)
{
	xmlChar *pTemp = NULL;
	pTemp = xmlGetProp(_pstXmlNode, (const xmlChar *)"name");    //获取name属性
	if (pTemp) {
		snprintf(_pStudent->acName, sizeof(_pStudent->acName), "%s", pTemp);
		xmlFree(pTemp);                                //释放
		pTemp = NULL;
	}

	pTemp = xmlGetProp(_pstXmlNode, (const xmlChar *)"age");    //获取age属性
	if (pTemp) {
		_pStudent->age = atoi((const char *)pTemp);
		xmlFree(pTemp);                                //释放
		pTemp = NULL;
	}	

	pTemp = xmlGetProp(_pstXmlNode, (const xmlChar *)"sex");    //获取sex属性
	if (pTemp) {
		snprintf(_pStudent->acSex, sizeof(_pStudent->acSex), "%s", pTemp);
		xmlFree(pTemp);                                //释放
		pTemp = NULL;
	}

	return 0;
}

解析XML文档的基本流程如下:

  1. 定义文档指针xmlDocPtr 和节点指针xmlNodePtr
  2. 调用xmlParseFile(),获取 xmlDocPtr 指针;
  3. 调用xmlDocGetRootElement()获取文档根节点,若无根节点则释放文档树并返回;
  4. 通过检查根节点名称,来判断文档正确性;
  5. pstXmlNode = pstXmlNode->xmlChildrenNode 获取子节点,并判断节点名;
  6. 调用 xmlGetProp() 获取节点属性;
  7. 遍历子节点内容,通过pstXmlNode = pstXmlNode->next不断向前遍历,直到pstXmlNode==NULL。查找找指定节点时使用 xmlStrcmp() 函数比较;
  8. 调用xmlFreeDoc()释放文档树指针;

2、结果测试

测试源代码:https://download.csdn.net/download/hinewcc/88908680

2.1 编译

$ gcc -o xml_parse xml_parse.c -I /usr/include/libxml2/ -L /usr/lib/x86_64-linux-gnu -lxml2

-I /usr/include/libxml2/ 原因:libxml/tree.h 和 libxml/parser.h 在 /usr/include/libxml2/ 下;

-L /usr/lib/x86_64-linux-gnu 原因:libxml2.so 在 /usr/lib/x86_64-linux-gnu 下;

编译后,生成 xml_parse 可执行程序。

2.2 运行

$ ./xml_parse file.xml

 打印结果:

grade : 8
class : 5
name : ZhangSan
age : 15
sex : male
name : LiSi
age : 16
sex : female

 测试过程如图:

三、注意点

1、libxml编程--内存泄漏问题

        调用xmlGetProp()函数会返回一个xmlChar类型的指针,这里动态分配了内存,这个指针使用完成之后一定要使用xmlFree()手动释放,其它返回指针类型的函数,使用时都要注意。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值