libxml2实例详解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
libxml2实例详解
最近因为资源区的一个天气预报的项目需要使用libxml2库来解析合作方提供的xml格式的文件,于是对它进行了一些研究。
下面的文章总结了网上的一些文章并结合我们自己的uconv库来进行讲解,希望能够起到抛砖引玉的作用,这样大家以后遇到类似的问题可以不用去找人请教了。
本片文章只涉及最普遍的应用,不涉及一些复杂功能如XPATH等功能。
先来说说libxml2的简介吧,libxml2是一个C语言的XML程序库,可以简单方便的提供对XML 文档的各种操作,并且支持XPATH查询,以及部分的支持XSLT转换等功能。
在我们的公共库的third目录下有该库,因此不需要我们自己下载或者安装。
Libxml2中的数据类型和函数
一个函数库中可能有几百种数据类型以及几千个函数,但是记住大师的话,90%的功能都是由30%的内容提供的。
对于libxml2,我认为搞懂以下的数据类型和函数就足够了。
内部字符类型xmlChar
xmlChar是Libxml2中的字符类型,库中所有字符、字符串都是基于这个数据类型。
事实上它的定义是:xmlstring.h
typedef unsigned char xmlChar;
使用unsigned char作为内部字符格式是考虑到它能很好适应UTF-8编码,而UTF-8编码正是libxml2的内部编码,其它格式的编码要转换为这个编码才能在libxml2中使用。
还经常可以看到使用xmlChar*作为字符串类型,很多函数会返回一个动态分配内存的xmlChar*变量,使用这样的函数时记得要手动删除内存。
xmlChar相关函数
如同标准c中的char类型一样,xmlChar也有动态内存分配、字符串操作等相关函数。
例如xmlMalloc是动态分配内存的函数;xmlFree是配套的释放内存函数;xmlStrcmp是字符串比较函数等等。
基本上xmlChar字符串相关函数都在xmlstring.h中定义;而动态内存分配函数在xmlmemory.h中定义。
xmlChar*与其它类型之间的转换
另外要注意,因为总是要在xmlChar*和char*之间进行类型转换,所以定义了一个宏
BAD_CAST,其定义如下:xmlstring.h
#define BAD_CAST (xmlChar *)
原则上来说,unsigned char和char之间进行强制类型转换是没有问题的。
文档类型xmlDoc、指针xmlDocPtr
xmlDoc是一个struct,保存了一个xml的相关信息,例如文件名、文档类型、子节点等等;xmlDocPtr等于xmlDoc*,它搞成这个样子总让人以为是智能指针,其实不是,要手动删除的。
xmlNewDoc函数创建一个新的文档指针。
xmlParseFile函数以默认方式读入一个UTF-8格式的文档,并返回文档指针。
xmlReadFile函数读入一个带有某种编码的xml文档,并返回文档指针;细节见libxml2参考手册。
xmlFreeDoc释放文档指针。
特别注意,当你调用xmlFreeDoc时,该文档所有包含的节点内存都被释放,所以一般来说不需要手动调用xmlFreeNode或者xmlFreeNodeList来释放动态分配的节点内存,除非你把该节点从文档中移除了。
一般来说,一个文档中所有节点都应该动态分配,然后加入文档,最后调用xmlFreeDoc一次释放所有节点申请的动态内存,这也是为什么我们很少看见xmlNodeFree的原因。
xmlSaveFile将文档以默认方式存入一个文件。
xmlSaveFormatFileEnc可将文档以某种编码/格式存入一个文件中。
节点类型xmlNode、指针xmlNodePtr
节点应该是xml中最重要的元素了,xmlNode代表了xml文档中的一个节点,实现为一个struct,内容很丰富:tree.h
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 */
};
可以看到,节点之间是以链表和树两种方式同时组织起来的,next和prev指针可以组成链表,而parent和children可以组织为树。
同时还有以下重要元素:
●节点中的文字内容:content;
●节点所属文档:doc;
●节点名字:name;
●节点的namespace:ns;
●节点属性列表:properties;
Xml文档的操作其根本原理就是在节点之间移动、查询节点的各项信息,并进行增加、删除、修改的操作。
xmlDocSetRootElement函数可以将一个节点设置为某个文档的根节点,这是将文档与节点连接起来的重要手段,当有了根结点以后,所有子节点就可以依次连接上根节点,从而组织成为一个xml树。
一些具体函数如如xmlNodeGetContent、xmlGetProp的使用可以参考该库的 .h文件,这里处于篇幅原因就不再一一介绍。
讲了这些数据结构和数据类型后,该轮到我们大显身手编码了,下面是我们需要处理的xml文件的一个片断,只选取了两条数据,我们的目标是提取出每个城市对应的url、今明两天的最低、最高气温、白天天气、晚上天气以及风力的数据。
<?xml version="1.0" encoding="gb2312" ?>
<T7ONLINE Typ="city" Date="2008-02-26 15:25:23">
<city Name="北京"
URL="/wcity/Beijing/Peking.html">
<forecast Date="2008-02-26" Tmax="10" Tmin="-2" Day="晴" Night="晴" Wind="西北风3-4级转西风2-3级" />
<forecast Date="2008-02-27" Tmax="13" Tmin="-1" Day="晴" Night="晴" Wind="西风转西南风2级" />
</city>
<city Name="顺义"
URL="/wcity/Beijing/Shunyi.html">
<forecast Date="2008-02-26" Tmax="9" Tmin="-1" Day="晴" Night="晴" Wind="西风4-5级转3-4级, 阵风8级" />
<forecast Date="2008-02-27" Tmax="13" Tmin="-1" Day="晴" Night="晴" Wind="西风3级" />
</city>
</T7ONLINE>
下面是完成该功能的代码,很简单,我想聪明的baiduer一眼就能看明白,需要注意的是,我们从libxml函数里读出来的字符时utf-8编码的,所以需要使用uconv库来进行到gbk的转码。
另外需要提的一点是,libxml2需要libz和libm库的支持,所以需要加上-lm和-lz进行编译。
/**********************************************************************
*
* Copyright (c) 2008 , Inc. All Rights Reserved
* $Id: comdg.vim,v 1.1 2007/11/27 04:12:15 baonh Exp $
*
**********************************************************************/
/**
* @file parserXml.cpp
* @author jipch(jipengcheng@)
* @date 2008/02/28 17:52:50
* @version $Revision: 1.1 $
* @brief
**/
#include "uconv.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <stdlib.h>
#include <stdio.h>
char* convert_utf82gbk(char *src, int decode_flag)
{
size_t len = strlen(src);
char *des = NULL;
des = (char *) malloc(len*3);
if (des)
{
if (utf8_to_gbk(src, len, des, len*3, decode_flag) > 0)
return des;
}
return NULL;
}
int main(int argc, char* argv[])
{
xmlDocPtr doc; //定义解析文档指针
xmlNodePtr curNode; //定义结点指针(你需要它为了在各个结点间移动)
xmlNodePtr lowNode;
char *szDocName;
int i = 0;
if (argc <= 1) {
printf("Usage: %s doc name.\n", argv[0]);
return(0);
}
szDocName = argv[1];
doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析文件if (NULL == doc) {
fprintf(stderr,"Document not parsed successfully.\n");
return -1;
}
curNode = xmlDocGetRootElement(doc); //确定文档根元素
/*检查确认当前文档中包含内容*/
if (NULL == curNode) {
fprintf(stderr,"empty document.\n");
xmlFreeDoc(doc);
return -1;
}
if (xmlStrcmp(curNode->name, BAD_CAST "T7ONLINE")) {
fprintf(stderr,"document of the wrong type, root node != root");
xmlFreeDoc(doc);
return -1;
}
curNode = curNode->xmlChildrenNode;
while(curNode != NULL)
{
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"city")))
{
i++;
xmlChar* szAttr = xmlGetProp(curNode,BAD_CAST"Name");
char* name = convert_utf82gbk((char *)szAttr, 0);
if(name)
{
printf("get Name = %s\n", name);
free(name);
}
xmlFree(szAttr);
szAttr = xmlGetProp(curNode,BAD_CAST"URL");
printf("get URL = %s\n", szAttr);
xmlFree(szAttr);
lowNode = curNode->children;
while(lowNode != NULL)
{
if ((!xmlStrcmp(lowNode->name, (const xmlChar *)"forecast"))) {
xmlChar* szAttr = xmlGetProp(lowNode,BAD_CAST "Date");
printf("get Date = %s\n", szAttr);
xmlFree(szAttr);
szAttr = xmlGetProp(lowNode,BAD_CAST "Tmax");
printf("get Tmax = %s\n", szAttr);
xmlFree(szAttr);
szAttr = xmlGetProp(lowNode,BAD_CAST "Tmin");
printf("get Tmin = %s\n", szAttr);
xmlFree(szAttr);
szAttr = xmlGetProp(lowNode,BAD_CAST "Day");
char*day = convert_utf82gbk((char *)szAttr, 0);
if(day)
{
printf("get Day = %s\n", day);
free(day);
day = NULL;
}
xmlFree(szAttr);
szAttr = xmlGetProp(lowNode,BAD_CAST "Night");
day = convert_utf82gbk((char *)szAttr, 0);
if(day)
{
printf("get Night = %s\n", day);
free(day);
day = NULL;
}
xmlFree(szAttr);
szAttr = xmlGetProp(lowNode,BAD_CAST "Wind");
day = convert_utf82gbk((char *)szAttr, 0);
if(day)
{
printf("get Wind = %s\n", day);
free(day);
}
xmlFree(szAttr);
}
lowNode = lowNode->next;
}
}
curNode = curNode->next;
}
xmlFreeDoc(doc);
printf("%d\n", i);
return 0;
}。