示例文档story.xml如下:
<?xml version="1.0"?> <story> <storyinfo> <author>John Fleck</author> <datewritten>June 2, 2002</datewritten> <keyword>example keyword</keyword> </storyinfo> <body> <headline>This is the headline</headline> <para>This is the body text.</para> </body> </story>1、解析xml文档
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <libxml/xmlmemory.h> #include <libxml/parser.h> /* 解析storyinfo节点,打印keyword节点的内容 */ void parseStory(xmlDocPtr doc, xmlNodePtr cur){ xmlChar* key; cur=cur->xmlChildrenNode; while(cur != NULL){ /* 找到keyword子节点 */ if(!xmlStrcmp(cur->name, (const xmlChar *)"keyword")){ key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); printf("keyword: %s\n", key); xmlFree(key); } cur=cur->next; /* 下一个子节点 */ } return; } /* 解析文档 */ static void parseDoc(char *docname){ /* 定义文档和节点指针 */ xmlDocPtr doc; xmlNodePtr cur; /* 进行解析,如果没成功,显示一个错误并停止 */ doc = xmlParseFile(docname); if(doc == NULL){ fprintf(stderr, "Document not parse successfully. \n"); return; } /* 获取文档根节点,若无内容则释放文档树并返回 */ cur = xmlDocGetRootElement(doc); if(cur == NULL){ fprintf(stderr, "empty document\n"); xmlFreeDoc(doc); return; } /* 确定根节点名是否为story,不是则返回 */ if(xmlStrcmp(cur->name, (const xmlChar *)"story")){ fprintf(stderr, "document of the wrong type, root node != story"); xmlFreeDoc(doc); return; } /* 遍历文档树 */ cur = cur->xmlChildrenNode; while(cur != NULL){ /* 找到storyinfo子节点 */ if(!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo")){ parseStory(doc, cur); /* 解析storyinfo子节点 */ } cur = cur->next; /* 下一个子节点 */ } xmlFreeDoc(doc); /* 释放文档树 */ return; } int main(int argc, char **argv){ char *docname; if(argc <= 1){ printf("Usage: %s docname\n", argv[0]); return 0; } docname=argv[1]; parseDoc(docname); return 1; }解析XML文档的基本流程如下:
#include <libxml/parser.h> #include <libxml/xpath.h> /* 解析文档 */ xmlDocPtr getdoc(char *docname){ xmlDocPtr doc; doc = xmlParseFile(docname); if(doc == NULL){ fprintf(stderr, "Document not parsed successfully. \n"); return NULL; } return doc; } /* 查询节点集 */ xmlXPathObjectPtr getnodeset(xmlDocPtr doc, xmlChar *xpath){ xmlXPathContextPtr context; xmlXPathObjectPtr result; /* 存储查询结果 */ /* 创建一个xpath上下文 */ context = xmlXPathNewContext(doc); if(context == NULL){ printf("Error in xmlXPathNewContext\n"); return NULL; } /* 查询XPath表达式 */ result = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context); /* 释放上下文指针 */ if(result == NULL){ printf("Error in xmlXPathEvalExpression\n"); return NULL; } /* 检查结果集是否为空 */ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ xmlXPathFreeObject(result); /* 如为这空就释放 */ printf("No result\n"); return NULL; } return result; } int main(int argc, char ** argv){ char *docname; xmlDocPtr doc; /* 查找所有keyword元素,而不管它们在文档中的位置 */ xmlChar *xpath=(xmlChar*)"//keyword"; xmlNodeSetPtr nodeset; xmlXPathObjectPtr result; int i; xmlChar *keyword; if(argc <= 1){ printf("Usage: %s docname\n", argv[0]); return(0); } docname = argv[1]; doc = getdoc(docname); result = getnodeset(doc, xpath); if(result){ /* 得到keyword节点集 */ nodeset = result->nodesetval; for(i=0; i < nodeset->nodeNr; i++){ /* 打印每个节点中的内容 */ keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1); printf("keyword: %s\n", keyword); xmlFree(keyword); } xmlXPathFreeObject(result); /* 释放结果集 */ } xmlFreeDoc(doc); /* 释放文档树 */ xmlCleanupParser(); /* 清除库内存 */ return(1); }可以在story.xml中多插入几个keyword元素,然后运行一下本程序看看效果。使用XPath查询信息的基本流程如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <libxml/xmlmemory.h> #include <libxml/parser.h> void parseStory(xmlDocPtr doc, xmlNodePtr cur, const xmlChar* keyword) { /* 在当前节点下插入一个keyword子节点 */ xmlNewTextChild(cur, NULL, (const xmlChar*)"keyword", keyword); return; } xmlDocPtr parseDoc(char *docname, char *keyword) { xmlDocPtr doc; xmlNodePtr cur; doc = xmlParseFile(docname); if (doc == NULL ) { fprintf(stderr,"Document not parsed successfully. \n"); return (NULL); } cur = xmlDocGetRootElement(doc); if (cur == NULL) { fprintf(stderr,"empty document\n"); xmlFreeDoc(doc); return (NULL); } if (xmlStrcmp(cur->name, (const xmlChar *) "story")) { fprintf(stderr,"document of the wrong type, root node != story"); xmlFreeDoc(doc); return (NULL); } cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo"))){ parseStory (doc, cur, (const xmlChar*)keyword); } cur = cur->next; } return(doc); } int main(int argc, char **argv) { char *docname; char *keyword; xmlDocPtr doc; if (argc <= 2) { printf("Usage: %s docname, keyword\n", argv[0]); return(0); } docname = argv[1]; keyword = argv[2]; doc = parseDoc(docname, keyword); if (doc != NULL) { xmlSaveFormatFile(docname, doc, 0); xmlFreeDoc(doc); } return (1); }这里xmlNewTextChild函数在当前节点指针上添加一个子元素。如果希望元素有名字空间,则可以在这里加上。添加完后,就要用xmlSaveFormatFile()把修改后的文档写入到文件。我们这里使用原来doc文档指针,因此会覆盖原来的文件。第三个参数如果设置为1,则输出的文档会自动缩进。
if(!xmlStrcmp(cur->name, BAD_CAST "keyword")){ xmlNodePtr tempNode; tempNode = cur->next; xmlUnlinkNode(cur); xmlFreeNode(cur); cur = tempNode; continue; }注意libxml2并没有xmlDelNode或者xmlRemoveNode之类的函数。我们需要将当前节点从文档中断链(unlink),文档就不会再包含这个子节点。这样做需要使用一个临时变量来存储断链节点的后续节点,并记得要手动删除断链节点的内存。
xmlDocPtr parseDoc(char *docname, char *uri) { xmlDocPtr doc; xmlNodePtr cur; xmlNodePtr newnode; xmlAttrPtr newattr; doc = xmlParseFile(docname); if (doc == NULL ) { fprintf(stderr,"Document not parsed successfully. \n"); return (NULL); } cur = xmlDocGetRootElement(doc); if (cur == NULL) { fprintf(stderr,"empty document\n"); xmlFreeDoc(doc); return (NULL); } if (xmlStrcmp(cur->name, (const xmlChar *) "story")) { fprintf(stderr,"document of the wrong type, root node != story"); xmlFreeDoc(doc); return (NULL); } newnode = xmlNewTextChild(cur, NULL, "reference", NULL); newattr = xmlNewProp(newnode, "uri", uri); return(doc); }我们用xmlAttrPtr声明一个属性指针。在找到story元素后,用xmlNewTextChild()新建一个reference子元素,用xmlNewProp()给这个子元素新建一个uri属性。文档修改完后要用xmlSaveFormatFile()写入到磁盘。
void getReference(xmlDocPtr doc, xmlNodePtr cur) { xmlChar *uri; cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"reference"))) { uri = xmlGetProp(cur, "uri"); printf("uri: %s\n", uri); xmlFree(uri); } cur = cur->next; } return; }关键函数为xmlGetProp(),用来获取节点中的指定属性。注意如果你使用DTD为属性声明一个固定的或默认的值,则该函数也查找这些值。
#include <stdio.h> #include <iostream> #include <libxml/parser.h> #include <libxml/tree.h> using namespace std; int main(int argc, char* argv[]){ //定义文档和节点指针 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; }编译并运行这个程序,将创建CreatedXml.xml文档,内容如下:
<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>注意,有多种方式可以添加子节点。第一是用xmlNewTextChild直接添加一个文本子节点;第二是先创建新节点,然后用xmlAddChild将新节点加入上层节点。
#include <string.h> #include <libxml/parser.h> /* 对指定编码格式的外部数据,转换成libxml使用UTF-8格式 */ unsigned char* convert(unsigned char *in, char *encoding){ unsigned char *out; int ret,size,out_size,temp; /* 定义一个编码处理器指针 */ xmlCharEncodingHandlerPtr handler; size = (int)strlen((const char*)in)+1; /* 输入数据长度 */ out_size = size*2-1; /* 输出数据长度 */ out = (unsigned char*)malloc((size_t)out_size); /* 存放输出数据 */ if (out) { /* 查找内建的编码处理器 */ handler = xmlFindCharEncodingHandler(encoding); if(!handler) { free(out); out = NULL; } } if(out) { temp=size-1; /* 对输入数据进行编码转换 */ ret = handler->input(out, &out_size, in, &temp); if(ret || temp-size+1) { /* 转换不成功 */ if (ret) { /* 转换失败 */ printf("conversion wasn't successful.\n"); } else { /* 只转换了一部分数据 */ printf("conversion wasn't successful. converted: %i octets.\n",temp); } free(out); out = NULL; }else { /* 转换成功 */ out = (unsigned char*)realloc(out,out_size+1); out[out_size]=0; /* 输出的末尾加上null终止符 */ } } else { printf("no mem\n"); } return (out); } int main(int argc, char **argv) { unsigned char *content, *out; xmlDocPtr doc; xmlNodePtr rootnode; char *encoding = "ISO-8859-1"; if (argc <= 1) { printf("Usage: %s content\n", argv[0]); return(0); } content = (unsigned char*)argv[1]; /* 转换成libxml2使用的UTF-8格式 */ out = convert(content, encoding); doc = xmlNewDoc (BAD_CAST "1.0"); rootnode = xmlNewDocNode(doc, NULL, (const xmlChar*)"root", out); xmlDocSetRootElement(doc, rootnode); /* 以ISO-8859-1格式输出文档内容 */ xmlSaveFormatFileEnc("-", doc, encoding, 1); return (1); }编译运行这个程序,假设在命令行上提供的数据"zhou"是ISO-8859-1格式(我的系统中不是),则输出文档为:
<?xml version="1.0" encoding="ISO-8859-1"?> <root>zhou</root>编码转换的基本流程如下:
#include <string.h> #include <iconv.h> #include <libxml/encoding.h> #include <libxml/xmlwriter.h> #include <libxml/xmlreader.h> /* 输入编码处理器:GBK到UTF-8 */ int gbk_input(unsigned char *out, int *outlen, const unsigned char *in, int *inlen){ char *outbuf = (char *) out; char *inbuf = (char *) in; iconv_t iconv_from; /* gbk到utf-8的转换描述符 */ size_t len1, len2, rslt; /* 注意一般不直接从int*到size_t*的转换 这在32位平台下是正常的,但到了64平台下size_t为64位, 那(size_t*)inlen将是一个未知的数据 */ len1 = *inlen; len2 = *outlen; /* 分配一个从GBK到UTF-8的转换描述符 */ iconv_from = iconv_open("utf-8","gbk"); /* 根据转换描述符,对数据进行编码转换 */ rslt = iconv(iconv_from, &inbuf, &len1, &outbuf, &len2); if(rslt < 0){ return rslt; } iconv_close(iconv_from); /* 释放描述符 */ *outlen = ((unsigned char *) outbuf - out); *inlen = ((unsigned char *) inbuf - in); return *outlen; } /* 输出编码处理器:UTF-8到GBK */ int gbk_output(unsigned char *out, int *outlen, const unsigned char *in, int *inlen){ char *outbuf = (char *) out; char *inbuf = (char *) in; iconv_t iconv_to; /* utf-8到gbk的转换描述符 */ size_t len1, len2, rslt; /* 注意一般不直接从int*到size_t*的转换 这在32位平台下是正常的,但到了64平台下size_t为64位, 那(size_t*)inlen将是一个未知的数据 */ len1 = *inlen; len2 = *outlen; /* 分配一个从UTF-8到GBK的转换描述符 */ iconv_to=iconv_open("gbk","utf-8"); /* 根据转换描述符,对数据进行编码转换 */ rslt = iconv(iconv_to, &inbuf, &len1, &outbuf, &len2); if(rslt < 0){ return rslt; } iconv_close(iconv_to); /* 释放描述符 */ *outlen = ((unsigned char *) outbuf - out); *inlen = ((unsigned char *) inbuf - in); return *outlen; } /** * convertToUTF8From: * 把encoding编码的输入数据in转换成utf-8格式返回 * 出错则返回NULL */ xmlChar *convertToUTF8From(const char *in, const char *encoding){ xmlChar *out; int ret; int size; int out_size; int temp; xmlCharEncodingHandlerPtr handler; if (in == 0) return 0; /* 查找内建的编码处理器 */ handler = xmlFindCharEncodingHandler(encoding); if (!handler) { printf("convertToUTF8From: no encoding handler found for '%s'\n", encoding ? encoding : ""); return 0; } size = (int)strlen(in) + 1; /* 输入数据长度 */ out_size = size*2 - 1; /* 输出数据长度 */ /* 存放输出数据 */ out = (unsigned char *) xmlMalloc((size_t) out_size); memset(out, 0, out_size); if(out != NULL) { temp = size - 1; /* 对输入数据进行编码转换,成功后返回0 */ ret = handler->input(out, &out_size, (const xmlChar *) in, &temp); if(ret || temp - size + 1) { /* 转换不成功 */ if(ret){ /* 转换失败 */ printf("convertToUTF8From: conversion wasn't successful.\n"); }else{ /* 只转换了一部分数据 */ printf("convertToUTF8From: conversion wasn't successful. converted: %i octets.\n", temp); } xmlFree(out); /* 释放输出缓冲区 */ out = 0; }else{ /* 转换成功,在输出末尾加上null终止符 */ out = (unsigned char *) xmlRealloc(out, out_size + 1); out[out_size] = 0; } } else { printf("convertToUTF8From: no mem\n"); } return out; } /** * utf8ConvertTo: * 把utf-8的数据转换成encoding编码返回 * 出错则返回NULL */ char *utf8ConvertTo(xmlChar *in, const char *encoding){ char *out; int ret; int size; int out_size; int temp; xmlCharEncodingHandlerPtr handler; if (in == 0) return 0; handler = xmlFindCharEncodingHandler(encoding); if (!handler) { printf("utf8ConvertTo: no encoding handler found for '%s'\n", encoding ? encoding : ""); return 0; } size = (int) strlen((char*)in) + 1; /* 输入数据长度 */ out_size = size * 2 - 1; /* 输出数据长度 */ out = (char *) malloc((size_t) out_size); /* 存放输出数据 */ memset(out,0,out_size); if(out != NULL) { temp = size - 1; /* 对输入数据进行编码转换,成功后返回0 */ ret = handler->output((xmlChar*)out, &out_size, (const xmlChar *) in, &temp); if(ret || temp - size + 1){ if(ret){ printf("utf8ConvertTo: conversion wasn't successful.\n"); }else{ printf("utf8ConvertTo: conversion wasn't successful. converted: %i octets.\n", temp); } free(out); out = 0; }else{ out = (char *) realloc(out, out_size + 1); out[out_size] = 0; /* 末尾加上null终止符 */ } }else{ printf("utf8ConvertTo: no mem\n"); } return out; } int main(int argc, char **argv){ const char *content; xmlChar *out; xmlDocPtr doc; xmlNodePtr rootnode; if (argc <= 1) { printf("Usage: %s content\n", argv[0]); return(0); } content = (const char*)argv[1]; /* 添加gbk编码支持 */ xmlNewCharEncodingHandler("gbk", gbk_input, gbk_output); /* 添加gb2312编码支持:仍然可以使用GBK的输入输出处理器 */ xmlNewCharEncodingHandler("gb2312", gbk_input, gbk_output); /* 输入的GBK数据转换成libxml2使用的UTF-8格式 */ out = convertToUTF8From(content, "gbk"); /* 创建xml文档 */ doc = xmlNewDoc(BAD_CAST "1.0"); rootnode = xmlNewDocNode(doc, NULL, (const xmlChar*)"root", out); xmlDocSetRootElement(doc, rootnode); /* 以gb2312格式保存文档内容:"-"表示输出到终端 */ xmlSaveFormatFileEnc("-", doc, "gb2312", 1); xmlCleanupCharEncodingHandlers();/* 释放编码处理器资源 */ return (1); }这个例子在32位与64位Linux平台下测试通过。iconv库是Linux默认自带的组件,因此在Linux中使用libxml非常方便。我们先建立utf-8编码与gbk编码的转换接口,并将接口插入到libxml2库中,这样xml库就支持对gb2312和gbk编码的支持了。当然,这个转换不会自动完成,我们需要使用从libxml库中查找特定编码的接口,libxml支持一些基本的编码接口,如ISO-8859-1,UTF-16等编码,但不支持gbk,所以在上述代码中,我们定义了gbk_input,与gbk_output两个接口,这两个接口的原型声明是libxml库的标准声明,即xmlCharEncodingInputFunc和xmlCharEncodingOutputFunc。在使用完libxml库之后,我们需要释放libxml库的转换资源。
#include <stdio.h> #include <string.h> #include <iconv.h> #include <libxml/parser.h> #include <libxml/tree.h> /* 代码转换:从一种编码转为另一种编码 */ int encoding_convert(const char *from_charset, const char *to_charset, char *inbuf, int inlen, char* outbuf, int outlen){ iconv_t cd; size_t len1, len2, rslt; /* 注意一般不直接从int*到size_t*的转换 这在32位平台下是正常的,但到了64平台下size_t为64位, 那(size_t*)inlen将是一个未知的数据 */ len1 = inlen; len2 = outlen; /* 分配一个转换描述符 */ cd = iconv_open(to_charset,from_charset); if(cd == 0) return -1; memset(outbuf,0,len2); /* 执行编码转换 */ rslt=iconv(cd, &inbuf, &len1, &outbuf, &len2); if(rslt== -1) return -1; iconv_close(cd); /* 释放描述符 */ return 0; } /* GB2312转换为UTF-8 * 成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL */ char *gb2312_utf8(char *inbuf){ int nOutLen = 2*strlen(inbuf)-1; char *szOut=(char*)xmlMalloc(nOutLen); if(-1 == encoding_convert("gb2312","uft-8",inbuf,strlen(inbuf),szOut,nOutLen)){ xmlFree(szOut); szOut=NULL; } return szOut; } /* UTF-8转换为GB2312 * 成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL */ char *utf8_gb2312(char *inbuf){ int nOutLen = 2* strlen(inbuf)-1; char *szOut=(char*)xmlMalloc(nOutLen); if(-1 == encoding_convert("utf-8","gb2312",inbuf,strlen(inbuf),szOut,nOutLen)){ xmlFree(szOut); szOut=NULL; } return szOut; } int main(int argc, char **argv){ /* 定义文档节点和指针 */ xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root_node=xmlNewNode(NULL, BAD_CAST "root"); /* 设置根节点 */ xmlDocSetRootElement(doc, root_node); /* 一个中文字符串转换为UTF-8字符串,然后写入 */ char *szOut=gb2312_utf8("节点1的内容"); /* 在根节点中直接创建节点 */ 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"); xmlNewChild(root_node, NULL, BAD_CAST "node1",BAD_CAST szOut); xmlFree(szOut); /* 创建一个节点,设置其内容和属性,然后加入根结点 */ xmlNodePtr node = xmlNewNode(NULL,BAD_CAST "node2"); xmlNodePtr content = xmlNewText(BAD_CAST "NODE CONTENT"); xmlAddChild(root_node,node); xmlAddChild(node,content); szOut = gb2312_utf8("属性值"); xmlNewProp(node,BAD_CAST "attribute",BAD_CAST szOut); xmlFree(szOut); /* 创建一个中文节点 */ szOut = gb2312_utf8("中文节点"); xmlNewChild(root_node, NULL, BAD_CAST szOut,BAD_CAST "content of chinese node"); xmlFree(szOut); /* 存储xml文档 */ int nRel = xmlSaveFormatFileEnc("CreatedXml_cn.xml",doc,"GB2312",1); if (nRel != -1){ printf("一个xml文档被创建,写入%d个字节", nRel); } xmlFreeDoc(doc); return 1; }这个例子中,当把中文数据写入到XML节点时,使用gb2312_utf8()直接转换成UTF-8格式,这种直接通过iconv转换的方式更高效。编译并运行程序,输出文档如下:
<?xml version="1.0" encoding="GB2312"?> <root> <newNode1>newNode1 content</newNode1> <newNode2>newNode2 content</newNode2> <newNode3>newNode3 content</newNode3> <node1>节点1的内容</node1> <node2 attribute="属性值">NODE CONTENT</node2> <中文节点>content of chinese node</中文节点> </root>6、一个真实的例子
<?xml version="1.0"?> <gjob:Helping xmlns:gjob="http://www.gnome.org/some-location"> <gjob:Jobs> <gjob:Job> <gjob:Project ID="3"/> <gjob:Application>GBackup</gjob:Application> <gjob:Category>Development</gjob:Category> <gjob:Update> <gjob:Status>Open</gjob:Status> <gjob:Modified>Mon, 07 Jun 1999 20:27:45 -0400 MET DST</gjob:Modified> <gjob:Salary>USD 0.00</gjob:Salary> </gjob:Update> <gjob:Developers> <gjob:Developer> </gjob:Developer> </gjob:Developers> <gjob:Contact> <gjob:Person>Nathan Clemons</gjob:Person> <gjob:Email>[email protected]</gjob:Email> <gjob:Company> </gjob:Company> <gjob:Organisation> </gjob:Organisation> <gjob:Webpage> </gjob:Webpage> <gjob:Snailmail> </gjob:Snailmail> <gjob:Phone> </gjob:Phone> </gjob:Contact> <gjob:Requirements> The program should be released as free software, under the GPL. </gjob:Requirements> <gjob:Skills> </gjob:Skills> <gjob:Details> A GNOME based system that will allow a superuser to configure compressed and uncompressed files and/or file systems to be backed up with a supported media in the system. This should be able to perform via find commands generating a list of files that are passed to tar, dd, cpio, cp, gzip, etc., to be directed to the tape machine or via operations performed on the filesystem itself. Email notification and GUI status display very important. </gjob:Details> </gjob:Job> </gjob:Jobs> </gjob:Helping>把XML文件加载到一个内部DOM树中只是调用几个函数的问题,而遍历整个树来收集数据,并生成内部结构则更困难,也更容易出错。
/* * 一个person记录 */ typedef struct person { char *name; char *email; char *company; char *organisation; char *smail; char *webPage; char *phone; } person, *personPtr; /* * 解析person的代码 */ personPtr parsePerson(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) { personPtr ret = NULL; DEBUG("parsePerson\n"); /* * 为结构分配内存 */ ret = (personPtr) malloc(sizeof(person)); if (ret == NULL) { fprintf(stderr,"out of memory\n"); return(NULL); } memset(ret, 0, sizeof(person)); /* 我们不关心顶层的元素名是什么 */ cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!strcmp(cur->name, "Person")) && (cur->ns == ns)) ret->name = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if ((!strcmp(cur->name, "Email")) && (cur->ns == ns)) ret->email = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); cur = cur->next; } return(ret); }下面是要注意的一些事项:
#include <libxml/tree.h> /* * 一个Job的描述 */ typedef struct job { char *projectID; char *application; char *category; personPtr contact; int nbDevelopers; personPtr developers[100]; /* using dynamic alloc is left as an exercise */ } job, *jobPtr; /* * 解析Job的代码 */ jobPtr parseJob(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur) { jobPtr ret = NULL; DEBUG("parseJob\n"); /* * 为结构分配内存 */ ret = (jobPtr) malloc(sizeof(job)); if (ret == NULL) { fprintf(stderr,"out of memory\n"); return(NULL); } memset(ret, 0, sizeof(job)); /* 我们不关心顶层元素名是什么 */ cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!strcmp(cur->name, "Project")) && (cur->ns == ns)) { ret->projectID = xmlGetProp(cur, "ID"); if (ret->projectID == NULL) { fprintf(stderr, "Project has no ID\n"); } } if ((!strcmp(cur->name, "Application")) && (cur->ns == ns)) ret->application = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if ((!strcmp(cur->name, "Category")) && (cur->ns == ns)) ret->category = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if ((!strcmp(cur->name, "Contact")) && (cur->ns == ns)) ret->contact = parsePerson(doc, ns, cur); cur = cur->next; } return(ret); }
一旦你会使用libxml2,编写这种类型的代码是非常简单的,也很无趣。最终,你可以写一个拥有C数据结构和一组XML文档例子或一个XML DTD的桩模块,并生成在C数据和XML存储之间导入和导出数据的代码。
7、详细代码示例
对Libxml2更详细的使用介绍,可参考官方的详细代码示例http://xmlsoft.org/examples/index.html。上面提供了Libxml2各个组件怎么使用的详细代码示例,包括以下部分:
xmlWriter: 测试xmlWriter的各个API,包括写入到文件、写入到内存缓冲区、写入到新的文档或子树、字符串编码转换、对输出文档进行序列化。
InputOutput: 演示使用xmlRegisterInputCallbacks来建立一个客户I/O层,这被用在XInclude方法上下文中,以显示怎样构建动态文档。还演示使用xmlDocDumpMemory来输出文档到字符缓冲区中。
Parsing: 演示使用xmlReadMemory()读取XML文档,xmlFreeDoc()释放文档树;使用xmlCreatePushParserCtxt()和xmlParseChunk()一块一块地读取XML文档到文档树中。演示为XML文档创建一个解析上下文,然后解析并验证这个文档;创建一个文档树,检查并验证结果,最后用xmlFreeDoc()释放文档树。演示使用xmlReadFile()读取XML文档并用xmlFreeDoc()释放它。
Tree: 演示怎样创建文档和节点,并把数据dump到标准输出或文件中。演示使用xmlDocGetRootElement()获取根元素,然后遍历文档并打印各个元素名。
XPath: 演示怎样计算XPath表达式,并在XPath上下文注册名称空间,打印结果节点集。演示怎么加载一个文档、用XPath定位到某个子元素、修改这个元素并保存结果。这包含了加载/编辑/保存的一个完整来回。
xmlReader: 演示使用xmlReaderForFile()解析XML文档,并dump出节点的信息。演示在用xmlReaderForFile()解析时验证文档的内容,激活各种选项,诸如实体替换、DTD属性不一致等。演示使用xmlTextReaderPreservePattern()提取XML文档中某一部分的子文档。演示重用xmlReader对象来解析多个XML文档。