Libxml2是一个C语言的XML程序库,可以简单方便的提供对XML文档的各种操作,并且支持XPATH查询,以及部分的支持XSLT转换等功能。
1. 下载与安装LIBXML2
Libxml2的下载地址是ftp://xmlsoft.org/libxml2/,完全版的库是开源的,官方网站:http://xmlsoft.org/。
下载最新的lib包(截止到目前为止libxml2-2.9.2.tar.gz是最新的)。
安装:
# ./configure (default install path is "/usr",if you want change install path,use --prefix)
# make
# make install
安装成功后,查看编译参数
# xml2-config --cflags
-I/usr/local/include/libxml2
# xml2-config --libs
-L/usr/local/lib -lxml2 -lz -lm
所以在编译自己的测试程序时,可以直接使用 #gcc -I /usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lz -lm -o test test.c 。
或者加入到makefile中,例如:
CFLAGS=`xml2-config --cflags`
LIBS=`xml2-config --libs`
2. Libxml2中的数据类型和函数
一个函数库中可能有几百种数据类型以及几千个函数,但是记住大师的话,90%的功能都是由30%的内容提供的。对于libxml2,我认为搞懂以下的数据类型和函数就足够了。
2.1 内部字符类型xmlChar
xmlChar是Libxml2中的字符类型,库中所有字符、字符串都是基于这个数据类型。事实上它的定义是:xmlstring.h
typedef unsigned char xmlChar;
使用unsigned char作为内部字符格式是考虑到它能很好适应UTF-8编码,而UTF-8编码正是libxml2的内部编码,其它格式的编码要转换为这个编码才能在libxml2中使用。
还经常可以看到使用xmlChar*作为字符串类型,很多函数会返回一个动态分配内存的xmlChar*变量,使用这样的函数时记得要手动删除内存。
2.2 xmlChar相关函数
如同标准c中的char类型一样,xmlChar也有动态内存分配、字符串操作等相关函数。例如xmlMalloc是动态分配内存的函数;xmlFree是配套的释放内存函数;xmlStrcmp是字符串比较函数等等。
基本上xmlChar字符串相关函数都在xmlstring.h中定义;而动态内存分配函数在xmlmemory.h中定义。
2.3 xmlChar*与其它类型之间的转换
另外要注意,因为总是要在xmlChar*和char*之间进行类型转换,所以定义了一个宏BAD_CAST,其定义如下:xmlstring.h
#define BAD_CAST (xmlChar *)
原则上来说,unsigned char和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
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可以组织为树。同时还有以下重要元素:
l 节点中的文字内容:content;
l 节点所属文档:doc;
l 节点名字:name;
l 节点的namespace:ns;
l 节点属性列表:properties;
Xml文档的操作其根本原理就是在节点之间移动、查询节点的各项信息,并进行增加、删除、修改的操作。
xmlDocSetRootElement函数可以将一个节点设置为某个文档的根节点,这是将文档与节点连接起来的重要手段,当有了根结点以后,所有子节点就可以依次连接上根节点,从而组织成为一个xml树。
2.6 节点集合类型xmlNodeSet、指针xmlNodeSetPtr
节点集合代表一个由节点组成的变量,节点集合只作为Xpath的查询结果而出现(XPATH的介绍见后面),因此被定义在xpath.h中,其定义如下:
/*
* A node-set (an unordered collection of nodes without duplicates).
*/
typedef struct _xmlNodeSet xmlNodeSet;
typedef xmlNodeSet *xmlNodeSetPtr;
struct _xmlNodeSet {
int nodeNr; /* number of nodes in the set */
int nodeMax; /* size of the array as allocated */
xmlNodePtr *nodeTab;/* array of nodes in no particular order */
/* @@ with_ns to check wether namespace nodes should be looked at @@ */
};
可以看出,节点集合有三个成员,分别是节点集合的节点数、最大可容纳的节点数,以及节点数组头指针。对节点集合中各个节点的访问方式很简单,如下:
xmlNodeSetPtr nodeset = XPATH查询结果;
for (int i = 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:2007 15:34
filename: CreateXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib
build: nmake TARGET_NAME=CreateXmlFile
purpose: 创建一个xml文件
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <iostream>
using namespace std;
int main()
{
//定义文档和节点指针
xmlDocPtr doc = xmlNewDoc(BAD_CAST"1.0");
xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST"root");
//设置根节点
xmlDocSetRootElement(doc,root_node);
//在根节点中直接创建节点
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode1", BAD_CAST "newNode1 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode2", BAD_CAST "newNode2 content");
xmlNewTextChild(root_node, NULL, BAD_CAST "newNode3", BAD_CAST "newNode3 content");
//创建一个节点,设置其内容和属性,然后加入根结点
xmlNodePtr node = xmlNewNode(NULL,BAD_CAST"node2");
xmlNodePtr content = xmlNewText(BAD_CAST"NODE CONTENT");
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);
xmlNodePtr grandson = xmlNewNode(NULL, BAD_CAST "grandson");
xmlAddChild(node,grandson);
xmlAddChild(grandson, xmlNewText(BAD_CAST "This is a grandson node"));
//存储xml文档
int nRel = xmlSaveFile("CreatedXml.xml",doc);
if (nRel != -1)
{
cout<<"一个xml文档被创建,写入"<<nRel<<"个字节"<<endl;
}
//释放文档内节点动态申请的内存
xmlFreeDoc(doc);
return 1;
}
编译链接命令如下:
g++ -o CreateXmlFile CreateXmlFile.cpp -I/usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lm -lz
然后执行可执行文件./CreateXmlFile,会生成一个xml文件CreatedXml.xml,打开后如下所示:
<?xml version="1.0"?> <root> <newNode1>newNode1 content</newNode1> <newNode2>newNode2 content</newNode2> <newNode3>newNode3 content</newNode3> <node2 attribute="yes">NODE CONTENT</node2> <son> <grandson>This is a grandson node</grandson> </son> </root>
3.2 解析xml文档
解析一个xml文档,从中取出想要的信息,例如节点中包含的文字,或者某个节点的属性,其流程如下:
l 用xmlReadFile函数读出一个文档指针doc;
l 用xmlDocGetRootElement函数得到根节点curNode;
l curNode->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:2007 11:47
filename: ParseXmlFile.cpp
author: Wang xuebin
depend: libxml2.lib
build: nmake TARGET_NAME=ParseXmlFile
purpose: 解析xml文件
*********************************************************************/
#include <libxml/parser.h>
#include <iostream.h>
int main(int argc, char* argv[])
{
xmlDocPtr doc; //定义解析文档指针
xmlNodePtr curNode; //定义结点指针(你需要它为了在各个结点间移动)
xmlChar *szKey; //临时字符串变量
char *szDocName;
if (argc <= 1)
{
printf("Usage: %s docname"n", argv[0]);
return(0);
}
szDocName = argv[1];
//下面两句是格式化xml的语句
xmlKeepBlanksDefault(0) ;
xmlIndentTreeOutput = 1 ;
doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析文件
//检查解析文档是否成功,如果不成功,libxml将指一个注册的错误并停止。
//一个常见错误是不适当的编码。XML标准文档除了用UTF-8或UTF-16外还可用其它编码保存。
//如果文档是这样,libxml将自动地为你转换到UTF-8。更多关于XML编码信息包含在XML标准中.
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;
}
/*在这个例子中,我们需要确认文档是正确的类型。“root”是在这个示例中使用文档的根类型。*/
if (xmlStrcmp(curNode->name, BAD_CAST "root"))
{
fprintf(stderr,"document of the wrong type, root node != root");
xmlFreeDoc(doc);
return -1;
}
curNode = curNode->xmlChildrenNode;
xmlNodePtr propNodePtr = curNode;
while(curNode != NULL)
{
//取出节点中的内容
if ((!xmlStrcmp(curNode->name, (const xmlChar *)"newNode1")))
{
szKey = xmlNodeGetContent(curNode);
printf("newNode1: %s"n", szKey);
xmlFree(szKey);
}
//查找带有属性attribute的节点
if (xmlHasProp(curNode,BAD_CAST "attribute"))
{
propNodePtr = curNode;
}
curNode = curNode->next;
}
//查找属性
xmlAttrPtr attrPtr = propNodePtr->properties;
while (attrPtr != NULL)
{
if (!xmlStrcmp(attrPtr->name, BAD_CAST "attribute"))
{
xmlChar* szAttr = xmlGetProp(propNodePtr,BAD_CAST "attribute");
cout<<"get attribute = "<<szAttr<<endl;
xmlFree(szAttr);
}
attrPtr = attrPtr->next;
}
xmlFreeDoc(doc);
return 0;
}
编译链接命令如下:
g++ -o ParseXmlFile ParseXmlFile.cpp -I/usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lm -lz
执行命令如下,使用第一次创建的xml文件作为输入:
./ParseXmlFile CreatedXml.xml
观察源代码可发现,所有以查询方式得到的xmlChar*字符串都必须使用xmlFree函数手动释放。否则会造成内存泄漏。
3.3 使用XPATH查找xml文档
简而言之,XPATH之于xml,好比SQL之于关系数据库。要在一个复杂的xml文档中查找所需的信息,XPATH简直是必不可少的工具。XPATH语法简单易学,并且有一个很好的官方教程,见http://www.zvon.org/xxl/XPathTutorial/Output_chi/introduction.html。这个站点的XML各种教程齐全,并且有包括中文在内的各国语言版本,真是让我喜欢到非常!
使用XPATH之前,必须首先熟悉几个数据类型和函数,它们是使用XPATH的前提。在libxml2中使用Xpath是非常简单的,其流程如下:
l 定义一个XPATH上下文指针xmlXPathContextPtr context,并且使用xmlXPathNewContext函数来初始化这个指针;
l 定义一个XPATH对象指针xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函数来计算Xpath表达式,得到查询结果,将结果存入对象指针中;
l 使用result->nodesetval得到节点集合指针,其中包含了所有符合Xpath查询结果的节点;
l 使用xmlXPathFreeContext释放上下文指针;
l 使用xmlXPathFreeObject释放Xpath对象指针;
具体的使用方法可以看XpathForXmlFile.cpp的这一段代码,其功能是查找符合某个Xpath语句的对象指针:
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc, const xmlChar *szXpath)
{
xmlXPathContextPtr context; //XPATH上下文指针
xmlXPathObjectPtr result; //XPATH对象指针,用来存储查询结果
context = xmlXPathNewContext(doc); //创建一个XPath上下文指针
if (context == NULL)
{
printf("context is NULL"n");
return NULL;
}
result = xmlXPathEvalExpression(szXpath, context); //查询XPath表达式,得到一个查询结果
xmlXPathFreeContext(context); //释放上下文指针
if (result == NULL)
{
printf("xmlXPathEvalExpression return NULL"n");
return NULL;
}
if (xmlXPathNodeSetIsEmpty(result->nodesetval)) //检查查询结果是否为空
{
xmlXPathFreeObject(result);
printf("nodeset is empty"n");
return NULL;
}
return result;
}
3.4 修改xml文档
根据上面的xpath找到对应的xml节点,修改属性值,并保存到文件中,代码如下(updatexmlproperty.cpp):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc,const xmlChar* xpathExpr){
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
assert(xpathExpr);
if (doc == NULL) {
return NULL;
}
/* Create xpath evaluation context */
xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context\n");
xmlFreeDoc(doc);
return NULL;
}
/* Evaluate xpath expression */
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
xmlXPathFreeContext(xpathCtx);
if(xpathObj == NULL) {
fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
xmlFreeDoc(doc);
return NULL;
}
return xpathObj;
}
void xmlSave(const char* filename,xmlDocPtr doc){
if(doc!=NULL){
if(filename != NULL && strlen(filename)!=0){
//save xml
xmlSaveFormatFileEnc(filename,doc,"UTF-8",1);
}
//free memory
xmlFreeDoc(doc);
xmlCleanupParser();
xmlMemoryDump();
}
}
int main(int argc, char *argv[]){
xmlDocPtr doc;
char *filename;
xmlChar *name; //property key
xmlChar *value; //property key
xmlChar *xpathExpr;
if (argc <= 4)
{
printf("Usage: %s xpathExpr\n", argv[0]);
return(0);
}
filename = argv[1];
xpathExpr = BAD_CAST argv[2];
name = BAD_CAST argv[3];
value = BAD_CAST argv[4];
printf("filename=%s,xpathExpr=%s,name=%s,value=%s\n",argv[1],argv[2],argv[3],argv[4]);
//format xml
xmlKeepBlanksDefault(0) ;
xmlIndentTreeOutput = 1 ;
doc = xmlReadFile(filename,"gb2312",XML_PARSE_RECOVER);
if (NULL == doc){
fprintf(stderr,"Document not parsed successfully\n");
return -1;
}
xmlXPathObjectPtr xmlPathPtr = getNodeSet(doc,xpathExpr);
if(xmlPathPtr==NULL){
return -1;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
if(curNode->type == XML_ELEMENT_NODE) {
xmlSetProp(curNode,name,value);
}
}
xmlSave(filename,doc);
return 0;
}
编译链接命令如下:
g++ -o updatexmlproperty updatexmlproperty.cpp -I/usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lm -lz
执行命令如下,使用第一次创建的xml文件作为输入:
./updatexmlproperty CreatedXml.xml //nod2 attribute no
结果如下(CreatedXml.xml):
<?xml version="1.0"?> <root> <newNode1>newNode1 content</newNode1> <newNode2>newNode2 content</newNode2> <newNode3>newNode3 content</newNode3> <node2 attribute="no">NODE CONTENT</node2> <son> <grandson>This is a grandson node</grandson> </son> </root>
3.5 删除xml文档
根据上面的xpath找到对应的xml节点,将当前节点从文档中断链(unlink),然后保存到文件中,代码如下(delxmlnode.cpp):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
xmlXPathObjectPtr getNodeSet(xmlDocPtr doc,const xmlChar* xpathExpr){
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
assert(xpathExpr);
if (doc == NULL) {
return NULL;
}
/* Create xpath evaluation context */
xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context\n");
xmlFreeDoc(doc);
return NULL;
}
/* Evaluate xpath expression */
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
xmlXPathFreeContext(xpathCtx);
if(xpathObj == NULL) {
fprintf(stderr,"Error:unable to evaluate xpath[%s] \n",xpathExpr);
xmlFreeDoc(doc);
return NULL;
}
return xpathObj;
}
void xmlSave(const char* filename,xmlDocPtr doc){
if(doc!=NULL){
if(filename != NULL && strlen(filename)!=0){
//save xml
xmlSaveFormatFileEnc(filename,doc,"UTF-8",1);
}
//free memory
xmlFreeDoc(doc);
xmlCleanupParser();
xmlMemoryDump();
}
}
int main(int argc, char *argv[]){
xmlDocPtr doc;
char *filename;
xmlChar *name; //property key
xmlChar *value; //property key
xmlChar *xpathExpr;
if (argc <= 2)
{
printf("Usage: %s xpathExpr\n", argv[0]);
return(0);
}
filename = argv[1];
xpathExpr = BAD_CAST argv[2];
if(argc>=3){
name = BAD_CAST argv[3];
}else{
name = BAD_CAST "";
}
if(argc>=4){
value = BAD_CAST argv[4];
}else{
value = BAD_CAST "";
}
//format xml
xmlKeepBlanksDefault(0) ;
xmlIndentTreeOutput = 1 ;
doc = xmlReadFile(filename,"gb2312",XML_PARSE_RECOVER);
if (NULL == doc){
fprintf(stderr,"Document not parsed successfully\n");
return -1;
}
xmlXPathObjectPtr xmlPathPtr = getNodeSet(doc,xpathExpr);
if(xmlPathPtr==NULL){
return -1;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
if(curNode->type == XML_ELEMENT_NODE) {
if((name!=NULL && strlen((char*)name)!=0) &&
(value!=NULL && strlen((char*)value)!=0)){
xmlAttrPtr attrs = curNode->properties;
while(NULL != attrs){
if (!xmlStrcmp(attrs->name, key)){
xmlChar* uqvalue = xmlGetProp(curNode,attrs->name);
if(!xmlStrcmp(value, uqvalue)){
//delete the node
xmlUnlinkNode(curNode);
xmlFreeNode(curNode);
}
}
attrs = attrs->next;
}
}else if(value!=NULL && strlen((char*)value)!=0){
//get node's text
xmlChar* uqvalue = xmlNodeGetContent(curNode);
if(!xmlStrcmp(value, uqvalue)){
//delete the node
xmlUnlinkNode(curNode);
xmlFreeNode(curNode);
}
}else{
//delete the node
xmlUnlinkNode(curNode);
xmlFreeNode(curNode);
}
}
}
xmlSave(filename,doc);
return 0;
}
编译链接命令如下:
g++ -o delxmlnode delxmlnode.cpp -I/usr/local/include/libxml2 -L/usr/local/lib -lxml2 -lm -lz
执行命令如下,使用第一次创建的xml文件作为输入:
./delxmlnode CreatedXml.xml //newNode3 (直接删除节点)
./delxmlnode CreatedXml.xml //newNode2 "" "newNode2 content" (验证节点文本后删除节点)
./delxmlnode CreatedXml.xml //node2 attribute no (验证节点属性后删除节点)
3.6 项目中总结的工具API
xml_parse.h
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
typedef unsigned int xsize;
xmlXPathObjectPtr execute_xpath_expression(xmlDocPtr doc,const xmlChar* xpathExpr);
xmlDocPtr getXmlDoc(const char* filename);
int xmlDeleteChildNode(const char* filename,const xmlChar* xpathExpr,const xmlChar* key, const xmlChar* value);
int xmlDeleteNode(const char* filename,const xmlChar* xpathExpr,const xmlChar* key, const xmlChar* value);
int xmlAddNode(const char* filename,const xmlChar* xpathExpr, xmlNodePtr curNode);
int xmlAddAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* key, const xmlChar* value);
int xmlEditAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* key, const xmlChar* value);
char *getXmlAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* key);
char **getXmlAttrs(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,xsize *len);
char **getXmlChildAttrs(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,xsize *len);
xmlNodePtr getXmlNode(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,const xmlChar* value);
int hasXmlNode(const char* filename, const xmlChar* xpathExpr);
void xmlSave(const char* filename,xmlDocPtr doc);
void xmlCreate(const char* filename,xmlNodePtr proNode);
xml_parse.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include "xml_parse.h"
xmlDocPtr getXmlDoc(const char* filename){
xmlDocPtr doc;
//set xml format
xmlKeepBlanksDefault(0) ;//libxml2 global variable .
xmlIndentTreeOutput = 1 ;// indent .with \n
/* Load XML document */
doc = xmlParseFile(filename);
if(doc==NULL){
fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
}
return doc;
}
xmlXPathObjectPtr execute_xpath_expression(xmlDocPtr doc,const xmlChar* xpathExpr){
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
assert(xpathExpr);
if (doc == NULL) {
return NULL;
}
/* Create xpath evaluation context */
xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context\n");
xmlFreeDoc(doc);
return NULL;
}
/* Evaluate xpath expression */
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
xmlXPathFreeContext(xpathCtx);
if(xpathObj == NULL) {
fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
xmlFreeDoc(doc);
return NULL;
}
return xpathObj;
}
int xmlDeleteChildNode(const char* filename, const xmlChar* xpathExpr, const xmlChar* key, const xmlChar* value){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
//if more node then xmlAddNextSibling
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
xmlNodePtr _curNode = curNode->children;
while (NULL != _curNode){
if(_curNode->type==XML_ELEMENT_NODE){
xmlAttrPtr attrs = _curNode->properties;
int isdel = 0;
while(NULL != attrs){
if (!xmlStrcmp(attrs->name, key)){
xmlChar* uqvalue = xmlGetProp(_curNode,attrs->name);
if(!xmlStrcmp(value, uqvalue)){
isdel = 1;
break;
}
}
attrs = attrs->next;
}
if(isdel){
result =1;
xmlNodePtr tempNode;
tempNode = _curNode->next;
xmlUnlinkNode(_curNode);
xmlFreeNode(_curNode);
_curNode = tempNode;
continue;
}
}
_curNode = _curNode->next;
}
}
xmlSave(filename,doc);
return result;
}
int xmlDeleteNode(const char* filename, const xmlChar* xpathExpr, const xmlChar* key, const xmlChar* value){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
//if more node then xmlAddNextSibling
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
int isdel = 0;
xmlAttrPtr attrs = curNode->properties;
while(NULL != attrs){
if (!xmlStrcmp(attrs->name, key)){
xmlChar* uqvalue = xmlGetProp(curNode,attrs->name);
if(!xmlStrcmp(value, uqvalue)){
isdel = 1;
break;
}
}
attrs = attrs->next;
}
if(isdel){
result =1;
xmlUnlinkNode(curNode);
xmlFreeNode(curNode);
}
}
xmlSave(filename,doc);
return result;
}
int xmlAddNode(const char* filename,const xmlChar* xpathExpr, xmlNodePtr curNode){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr _curNode;
int size = (nodes) ? nodes->nodeNr : 0;
//if more node then xmlAddNextSibling
int i;
if(size>1){
xmlNodePtr sibNode;
for(i=0;i<size;i++){
if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
_curNode = nodes->nodeTab[i];
if(xmlStrcmp(_curNode->name,curNode->name)){
xmlNodePtr _curChildNode = _curNode->children;
while (NULL != _curChildNode){
if(!xmlStrcmp(_curChildNode->name,curNode->name)){
sibNode = _curChildNode;
}
_curNode = _curNode->children;
}
}else{
sibNode = _curNode;
}
}
}
printf("node name=%s\n",(char*)sibNode->name);
//add brother node
if(sibNode!=NULL){
xmlAddSibling(sibNode,curNode);
result = 1;
}
}else{
_curNode = nodes->nodeTab[0];
xmlAddChild(_curNode,curNode);
result = 1;
}
xmlSave(filename,doc);
return result;
}
int xmlAddAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* name, const xmlChar* value){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
if(curNode->type == XML_ELEMENT_NODE) {
xmlNewProp(curNode,name,value);
}
}
xmlSave(filename,doc);
result = 1;
return result;
}
int xmlEditAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* name, const xmlChar* value){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
if(curNode->type == XML_ELEMENT_NODE) {
xmlSetProp(curNode,name,value);
}
}
xmlSave(filename,doc);
result = 1;
return result;
}
char *getXmlAttr(const char* filename,const xmlChar* xpathExpr,const xmlChar* name){
char *value = NULL;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return value;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
fprintf(stderr, "xpathExpr[%s] can't find node\n", xpathExpr);
return value;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
if(size>0){
curNode = nodes->nodeTab[0];
value = (char*)xmlGetProp(curNode,name);
}
xmlSave(NULL,doc);
return value;
}
char **getXmlAttrs(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,xsize *len){
char **value = NULL;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return value;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
fprintf(stderr, "xpathExpr[%s] can't find node\n", xpathExpr);
return value;
}
//attributes real length
int lens = 0;
value = (char**)malloc(N*sizeof(char*)); //malloc memory
int k;
for(k=0;k<N;k++)
{
char *tmp = (char*)malloc(sizeof(char*)*1024);
*(value+k) = tmp;
strcpy(*(value+k),"");
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
if(size>0){
int i;
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
if(curNode->type==XML_ELEMENT_NODE){
char *uqvalue = (char*)xmlGetProp(curNode,name);
*(value+i) = uqvalue;
lens++;
}
}
}
*len = lens;
xmlSave(NULL,doc);
return value;
}
xmlNodePtr getXmlNode(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,const xmlChar* value){
xmlNodePtr curNode = NULL;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return curNode;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
fprintf(stderr, "xpathExpr[%s] can't find node\n", xpathExpr);
return curNode;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr tempNode;
int size = (nodes) ? nodes->nodeNr : 0;
if(size>0){
int i;
for(i=0;i<size;i++){
tempNode = nodes->nodeTab[i];
if(tempNode->type==XML_ELEMENT_NODE){
xmlChar *uqvalue = xmlGetProp(tempNode,name);
if(xmlStrcmp(uqvalue,value)){
curNode = tempNode;
break;
}
}
}
}
xmlSave(NULL,doc);
return curNode;
}
char **getXmlChildAttrs(const char* filename,const xmlChar* xpathExpr,const xmlChar* name,xsize *len){
char **value = NULL;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return value;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
fprintf(stderr, "xpathExpr[%s] can't find node\n", xpathExpr);
return value;
}
//attributes real length
int lens = 0;
value = (char**)malloc(N*sizeof(char*)); //malloc memory
int k;
for(k=0;k<N;k++)
{
char *tmp = (char*)malloc(sizeof(char*)*1024);
*(value+k) = tmp;
strcpy(*(value+k),"");
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
xmlNodePtr curNode;
int size = (nodes) ? nodes->nodeNr : 0;
int i,j=0;
if(size>0){
for(i=0;i<size;i++){
curNode = nodes->nodeTab[i];
xmlNodePtr _curNode = curNode->children;
while (NULL != _curNode){
if(_curNode->type==XML_ELEMENT_NODE){
xmlAttrPtr attrs = _curNode->properties;
while(NULL != attrs){
if (!xmlStrcmp(attrs->name, name)){
char *uqvalue = (char*)xmlGetProp(_curNode,attrs->name);
*(value+j) = uqvalue;
lens++;
j++;
break;
}
attrs = attrs->next;
}
}
_curNode = _curNode->next;
}
}
}
*len = lens;
xmlSave(NULL,doc);
return value;
}
int hasXmlNode(const char* filename, const xmlChar* xpathExpr){
int result = 0;
xmlDocPtr doc = getXmlDoc(filename);
if(doc==NULL){
return result;
}
xmlXPathObjectPtr xmlPathPtr = execute_xpath_expression(doc,xpathExpr);
if(xmlPathPtr==NULL){
return result;
}
xmlNodeSetPtr nodes = xmlPathPtr->nodesetval;
int size = (nodes) ? nodes->nodeNr : 0;
if(size>0){
result = 1;
}
xmlSave(NULL,doc);
return result;
}
void xmlSave(const char* filename,xmlDocPtr doc){
if(doc!=NULL){
if(filename != NULL && strlen(filename)!=0){
//save xml
xmlSaveFormatFileEnc(filename,doc,"UTF-8",1);
}
//free memory
xmlFreeDoc(doc);
xmlCleanupParser();
xmlMemoryDump();
}
}
void xmlCreate(const char* filename,xmlNodePtr proNode){
xmlDocPtr pdoc = NULL;
xmlNodePtr proot_node = NULL;
pdoc = xmlNewDoc (BAD_CAST "1.0");
proot_node = xmlNewNode (NULL, BAD_CAST "manifest");
xmlDocSetRootElement (pdoc, proot_node);
xmlAddChild (proot_node, proNode);
xmlSave(&filename[0],pdoc);
}
4. 用ICONV解决XML中的中文问题
Libxml2中默认的内码是UTF-8,所有使用libxml2进行处理的xml文件,必须首先显式或者默认的转换为UTF-8编码才能被处理。
要在xml中使用中文,就必须能够在UTF-8和GB2312内码(较常用的一种简体中文编码)之间进行转换。Libxml2提供了默认的内码转换机制,并且在libxml2的Tutorial中有一个例子,事实证明这个例子并不适合用来转换中文。
所以需要我们显式的使用ICONV来进行内码转换,libxml2本身也是使用ICONV进行转换的。ICONV是一个专门用来进行编码转换的库,基本上支持目前所有常用的编码。它是glibc库的一个部分,常常被用于UNIX系统中。由于目前工作中还没有涉及到中文,所以这里就不再举例说明了,网上这种资料很多。
转载于:https://blog.51cto.com/jeff1573/1665859