C的xml编程libxml2.docx
《C的xml编程libxml2.docx》由会员分享,可在线阅读,更多相关《C的xml编程libxml2.docx(23页珍藏版)》请在冰点文库上搜索。
C的xml编程libxml2
C的xml编程-libxml2
C的xml编程-libxml2这里主要讲述libxml2在linux下的使用。
(以下内容除了linux下的安装步骤是自己写的,其余均出自
1.下载与安装LIBXML2
Libxml2是一个C语言的XML程序库,可以简单方便的提供对XML文档的各种操作,并且支持XPATH查询,以及部分的支持XSLT转换等功能。
Libxml2的下载地址是http:
//xmlsoft.org/,完全版的库是开源的,并且带有例子程序和说明文档。
最好将这个库先下载下来,因为这样可以查看其中的文档和例子。
由于我是在linux下用C语言进行开发的,所以我下载的是libxml2-2.6.20.tar.gz版本的源码包。
具体安装步骤:
1、解压:
$tarzxvflibxml2-2.6.20.tar.gz
2、进入解压后的安装目录:
$cdlibxml2-2.6.20
3、安装三部曲:
1)$./configure
2)$make
3)$makeinstall
安装完毕。
2.Libxml2中的数据类型和函数
一个函数库中可能有几百种数据类型以及几千个函数,但是记住大师的话,90%的功能都是由30%的内容提供的。
对于libxml2,我认为搞懂以下的数据类型和函数就足够了。
2.1内部字符类型xmlChar
xmlChar是Libxml2中的字符类型,库中所有字符、字符串都是基于这个数据类型。
事实上它的定义是:
xmlstring.h
typedefunsignedcharxmlChar;
使用unsignedchar作为内部字符格式是考虑到它能很好适应UTF-8编码,而UTF-8编码正是libxml2的内部编码,其它格式的编码要转换为这个编码才能在libxml2中使用。
还经常可以看到使用xmlChar*作为字符串类型,很多函数会返回一个动态分配内存的xmlChar*变量,使用这样的函数时记得要手动删除内存。
2.2xmlChar相关函数
如同标准c中的char类型一样,xmlChar也有动态内存分配、字符串操作等相关函数。
例如xmlMalloc是动态分配内存的函数;xmlFree是配套的释放内存函数;xmlStrcmp是字符串比较函数等等。
基本上xmlChar字符串相关函数都在xmlstring.h中定义;而动态内存分配函数在xmlmemory.h中定义。
2.3xmlChar*与其它类型之间的转换
另外要注意,因为总是要在xmlChar*和char*之间进行类型转换,所以定义了一个宏BAD_CAST,其定义如下:
xmlstring.h
#defineBAD_CAST(xmlChar*)
原则上来说,unsignedchar和char之间进行强制类型转换是没有问题的。
2.4文档类型xmlDoc、指针xmlDocPtr
xmlDoc是一个struct,保存了一个xml的相关信息,例如文件名、文档类型、子节点等等;xmlDocPtr等于xmlDoc*,它搞成这个样子总让人以为是智能指针,其实不是,要手动删除的。
xmlNewDoc函数创建一个新的文档指针。
xmlParseFile函数以默认方式读入一个UTF-8格式的文档,并返回文档指针。
xmlReadFile函数读入一个带有某种编码的xml文档,并返回文档指针;细节见libxml2参考手册。
xmlFreeDoc释放文档指针。
特别注意,当你调用xmlFreeDoc时,该文档所有包含的节点内存都被释放,所以一般来说不需要手动调用xmlFreeNode或者xmlFreeNodeList来释放动态分配的节点内存,除非你把该节点从文档中移除了。
一般来说,一个文档中所有节点都应该动态分配,然后加入文档,最后调用xmlFreeDoc一次释放所有节点申请的动态内存,这也是为什么我们很少看见xmlNodeFree的原因。
xmlSaveFile将文档以默认方式存入一个文件。
xmlSaveFormatFileEnc可将文档以某种编码/格式存入一个文件中。
2.5节点类型xmlNode、指针xmlNodePtr
节点应该是xml中最重要的元素了,xmlNode代表了xml文档中的一个节点,实现为一个struct,内容很丰富:
tree.h
typedefstruct_xmlNodexmlNode;
typedefxmlNode*xmlNodePtr;
struct_xmlNode{
void*_private;/*applicationdata*/
xmlElementTypetype;/*typenumber,mustbesecond!
*/
constxmlChar*name;/*thenameofthenode,ortheentity*/
struct_xmlNode*children;/*parent->childslink*/
struct_xmlNode*last;/*lastchildlink*/
struct_xmlNode*parent;/*child->parentlink*/
struct_xmlNode*next;/*nextsiblinglink*/
struct_xmlNode*prev;/*previoussiblinglink*/
struct_xmlDoc*doc;/*thecontainingdocument*/
/*Endofcommonpart*/
xmlNs*ns;/*pointertotheassociatednamespace*/
xmlChar*content;/*thecontent*/
struct_xmlAttr*properties;/*propertieslist*/
xmlNs*nsDef;/*namespacedefinitionsonthisnode*/
void*psvi;/*fortype/PSVIinformations*/
unsignedshortline;/*linenumber*/
unsignedshortextra;/*extradataforXPath/XSLT*/
};
可以看到,节点之间是以链表和树两种方式同时组织起来的,next和prev指针可以组成链表,而parent和children可以组织为树。
同时还有以下重要元素:
l节点中的文字内容:
content;
l节点所属文档:
doc;
l节点名字:
name;
l节点的namespace:
ns;
l节点属性列表:
properties;
Xml文档的操作其根本原理就是在节点之间移动、查询节点的各项信息,并进行增加、删除、修改的操作。
xmlDocSetRootElement函数可以将一个节点设置为某个文档的根节点,这是将文档与节点连接起来的重要手段,当有了根结点以后,所有子节点就可以依次连接上根节点,从而组织成为一个xml树。
2.6节点集合类型xmlNodeSet、指针xmlNodeSetPtr
节点集合代表一个由节点组成的变量,节点集合只作为Xpath的查询结果而出现(XPATH的介绍见后面),因此被定义在xpath.h中,其定义如下:
/*
*Anode-set(anunorderedcollectionofnodeswithoutduplicates).
*/
typedefstruct_xmlNodeSetxmlNodeSet;
typedefxmlNodeSet*xmlNodeSetPtr;
struct_xmlNodeSet{
intnodeNr;/*numberofnodesintheset*/
intnodeMax;/*sizeofthearrayasallocated*/
xmlNodePtr*nodeTab;/*arrayofnodesinnoparticularorder*/
/*@@with_nstocheckwethernamespacenodesshouldbelookedat@@*/
};
可以看出,节点集合有三个成员,分别是节点集合的节点数、最大可容纳的节点数,以及节点数组头指针。
对节点集合中各个节点的访问方式很简单,如下:
xmlNodeSetPtrnodeset=XPATH查询结果;
for(inti=0;i<nodeset->nodeNr;i++)
{
nodeset->nodeTab[i];
}
注意,libxml2是一个c函数库,因此其函数和数据类型都使用c语言的方式来处理。
如果是c++,我想我宁愿用STL中的vector来表示一个节点集合更好,而且没有内存泄漏或者溢出的担忧。
3.简单xml操作例子
了解以上基本知识之后,就可以进行一些简单的xml操作了。
当然,还没有涉及到内码转换(使得xml中可以处理中文)、xpath等较复杂的操作。
3.1创建xml文档
有了上面的基础,创建一个xml文档显得非常简单,其流程如下:
l用xmlNewDoc函数创建一个文档指针doc;
l用xmlNewNode函数创建一个节点指针root_node;
l用xmlDocSetRootElement将root_node设置为doc的根结点;
l给root_node添加一系列的子节点,并设置子节点的内容和属性;
l用xmlSaveFile将xml文档存入文件;
l用xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。
注意,有多种方式可以添加子节点:
第一是用xmlNewTextChild直接添加一个文本子节点;第二是先创建新节点,然后用xmlAddChild将新节点加入上层节点。
源代码文件是CreateXmlFile.cpp,如下:
/********************************************************************
created:
2007/11/09
created:
9:
11:
200715:
34
filename:
CreateXmlFile.cpp
author:
Wangxuebin
depend:
libxml2.lib
build:
nmakeTARGET_NAME=CreateXmlFile
purpose:
创建一个xml文件
*********************************************************************/
#include<stdio.h>
#include<libxml/parser.h>
#include<libxml/tree.h>
#include<iostream.h>
intmain()
{
//定义文档和节点指针
xmlDocPtrdoc=xmlNewDoc(BAD_CAST"1.0");
xmlNodePtrroot_node=xmlNewNode(NULL,BAD_CAST"root");
//设置根节点
xmlDocSetRootElement(doc,root_node);
//在根节点中直接创建节点
xmlNewTextChild(root_node,NULL,BAD_CAST"newNode1",BAD_CAST"newNode1content");
xmlNewTextChild(root_node,NULL,BAD_CAST"newNode2",BAD_CAST"newNode2content");
xmlNewTextChild(root_node,NULL,BAD_CAST"newNode3",BAD_CAST"newNode3content");
//创建一个节点,设置其内容和属性,然后加入根结点
xmlNodePtrnode=xmlNewNode(NULL,BAD_CAST"node2");
xmlNodePtrcontent=xmlNewText(BAD_CAST"NODECONTENT");
xmlAddChild(root_node,node);
xmlAddChild(node,content);
xmlNewProp(node,BAD_CAST"attribute",BAD_CAST"yes");
//创建一个儿子和孙子节点
node=xmlNewNode(NULL,BAD_CAST"son");
xmlAddChild(root_node,node);
xmlNodePtrgrandson=xmlNewNode(NULL,BAD_CAST"grandson");
xmlAddChild(node,grandson);
xmlAddChild(grandson,xmlNewText(BAD_CAST"Thisisagrandsonnode"));
//存储xml文档
intnRel=xmlSaveFile("CreatedXml.xml",doc);
if(nRel!
=-1)
{
cout<<"一个xml文档被创建,写入"<<nRel<<"个字节"<<endl;
}
//释放文档内节点动态申请的内存
xmlFreeDoc(doc);
return1;
}
编译链接命令如下:
nmakeTARGET_NAME=CreateXmlFile
然后执行可执行文件CreateXmlFile.exe,会生成一个xml文件CreatedXml.xml,打开后如下所示:
<?
xmlversion="1.0"?
>
<root>
<newNode1>newNode1content</newNode1>
<newNode2>newNode2content</newNode2>
<newNode3>newNode3content</newNode3>
<node2attribute="yes">NODECONTENT</node2>
<son>
<grandson>Thisisagrandsonnode</grandson>
</son>
</root>
最好使用类似XMLSPY这样的工具打开,因为这些工具可以自动整理xml文件的栅格,否则很有可能是没有任何换行的一个xml文件,可读性较差。
3.2解析xml文档
解析一个xml文档,从中取出想要的信息,例如节点中包含的文字,或者某个节点的属性,其流程如下:
l用xmlReadFile函数读出一个文档指针doc;
l用xmlDocGetRootElement函数得到根节点curNode;
lcurNode->xmlChildrenNode就是根节点的子节点集合;
l轮询子节点集合,找到所需的节点,用xmlNodeGetContent取出其内容;
l用xmlHasProp查找含有某个属性的节点;
l取出该节点的属性集合,用xmlGetProp取出其属性值;
l用xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。
注意:
节点列表的指针依然是xmlNodePtr,属性列表的指针也是xmlAttrPtr,并没有xmlNodeList或者xmlAttrList这样的类型。
看作列表的时候使用它们的next和prev链表指针来进行轮询。
只有在Xpath中有xmlNodeSet这种类型,其使用方法前面已经介绍了。
源代码如下:
ParseXmlFile.cpp
/********************************************************************
created:
2007/11/15
created:
15:
11:
200711:
47
filename:
ParseXmlFile.cpp
author:
Wangxuebin
depend:
libxml2.lib
build:
nmakeTARGET_NAME=ParseXmlFile
purpose:
解析xml文件
*********************************************************************/
#include<libxml/parser.h>
#include<iostream.h>
intmain(intargc,char*argv[])
{
xmlDocPtrdoc;//定义解析文档指针
xmlNodePtrcurNode;//定义结点指针(你需要它为了在各个结点间移动)
xmlChar*szKey;//临时字符串变量
char*szDocName;
if(argc<=1)
{
printf("Usage:
%sdocname"n",argv[0]);
return(0);
}
szDocName=argv[1];
doc=xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER);//解析文件
//检查解析文档是否成功,如果不成功,libxml将指一个注册的错误并停止。
//一个常见错误是不适当的编码。
XML标准文档除了用UTF-8或UTF-16外还可用其它编码保存。
//如果文档是这样,libxml将自动地为你转换到UTF-8。
更多关于XML编码信息包含在XML标准中.
if(NULL==doc)
{
fprintf(stderr,"Documentnotparsedsuccessfully."n");
return-1;
}
curNode=xmlDocGetRootElement(doc);//确定文档根元素
/*检查确认当前文档中包含内容*/
if(NULL==curNode)
{
fprintf(stderr,"emptydocument"n");
xmlFreeDoc(doc);
return-1;
}
/*在这个例子中,我们需要确认文档是正确的类型。
“root”是在这个示例中使用文档的根类型。
*/
if(xmlStrcmp(curNode->name,BAD_CAST"root"))
{
fprintf(stderr,"documentofthewrongtype,rootnode!
=root");
xmlFreeDoc(doc);
return-1;
}
curNode=curNode->xmlChildrenNode;
xmlNodePtrpropNodePtr=curNode;
while(curNode!
=NULL)
{
//取出节点中的内容
if((!
xmlStrcmp(curNode->name,(constxmlChar*)"newNode1")))
{
szKey=xmlNodeGetContent(curNode);
printf("newNode1:
%s"n",szKey);
xmlFree(szKey);
}
//查找带有属性attribute的节点
if(xmlHasProp(curNode,BAD_CAST"attribute"))
{
propNodePtr=curNode;
}
curNode=curNode->next;
}
//查找属性
xmlAttrPtrattrPtr=propNodePtr->properties;
while(attrPtr!
=NULL)
{
if(!
xmlStrcmp(attrPtr->name,BAD_CAST"attribute"))
{
xmlChar*szAttr=xmlGetProp(propNodePtr,BAD_CAST"attribute");
cout<<"getattribute="<<szAttr<<endl;
xmlFree(szAttr);
}
attrPtr=attrPtr->next;
}
xmlFreeDoc(doc);
return0;
}
编译链接命令如下:
nmakeTARGET_NAME=ParseXmlFile
执行命令如下,使用第一次创建的xml文件作为输入:
ParseXmlFile.exeCreatedXml.xml
观察源代码可发现,所有以查询方式得到的xmlChar*字符串都必须使用xmlFree函数手动释放。
否则会造成内存泄漏。
3.3修改xml文档
有了上面的基础,修改xml文档的内容就很简单了。
首先打开一个已经存在的xml文档,顺着根结点找到需要添加、删除、修改的地方,调用相应的xml函数对节点进行增、删、改操作。
源代码见ChangeXmlFile,编译链接方法如上。
执行下面的命令:
ChangeXmlFile.exeCreatedXml.xml
可以得到一个修改后的xml文档ChangedXml.xml,如下:
<?
xmlversion="1.0"?
>
<root>
<newNode2>contentchanged</newNode2>
<newNode3newAttr="YES">newNode3content</newNode3>
<node2attribute="no">NODECONTENT</node2>
<son>