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

xmlCharLibxml2中的字符类型,库中所有字符、字符串都是基于这个数据类型。事实上它的定义是: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 */
};

可以看到,节点之间是以链表和树两种方式同时组织起来的,nextprev指针可以组成链表,而parentchildren可以组织为树。同时还有以下重要元素:

l         节点中的文字内容:content

l         节点所属文档:doc

l         节点名字:name

l         节点的namespacens

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         xmlDocSetRootElementroot_node设置为doc的根结点;

l         root_node添加一系列的子节点,并设置子节点的内容和属性;

l         xmlSaveFilexml文档存入文件;

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这样的类型。看作列表的时候使用它们的nextprev链表指针来进行轮询。只有在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-8GB2312内码(较常用的一种简体中文编码)之间进行转换。Libxml2提供了默认的内码转换机制,并且在libxml2Tutorial中有一个例子,事实证明这个例子并不适合用来转换中文。

所以需要我们显式的使用ICONV来进行内码转换,libxml2本身也是使用ICONV进行转换的。ICONV是一个专门用来进行编码转换的库,基本上支持目前所有常用的编码。它是glibc库的一个部分,常常被用于UNIX系统中。由于目前工作中还没有涉及到中文,所以这里就不再举例说明了,网上这种资料很多。