目录
详细教程请参考: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.读取"class"节点 */
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文档的基本流程如下:
- 定义文档指针xmlDocPtr 和节点指针xmlNodePtr;
- 调用xmlParseFile(),获取 xmlDocPtr 指针;
- 调用xmlDocGetRootElement()获取文档根节点,若无根节点则释放文档树并返回;
- 通过检查根节点名称,来判断文档正确性;
- pstXmlNode = pstXmlNode->xmlChildrenNode 获取子节点,并判断节点名;
- 调用 xmlGetProp() 获取节点属性;
- 遍历子节点内容,通过pstXmlNode = pstXmlNode->next不断向前遍历,直到pstXmlNode==NULL。查找找指定节点时使用 xmlStrcmp() 函数比较;
- 调用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()手动释放,其它返回指针类型的函数,使用时都要注意。