- 浏览: 151343 次
- 性别:
- 来自: 北京
文章分类
最新评论
libxml2剖析(3):使用教程
示例文档story.xml如下:
- <?xmlversion="1.0"?>
- <story>
- <storyinfo>
- <author>JohnFleck</author>
- <datewritten>June2,2002</datewritten>
- <keyword>examplekeyword</keyword>
- </storyinfo>
- <body>
- <headline>Thisistheheadline</headline>
- <para>Thisisthebodytext.</para>
- </body>
- </story>
<?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文档
解析文档时只需要文档名和一个函数调用,再加上错误处理。下面代码查找keyword节点并打印节点下的文本内容,如下:
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #include<libxml/xmlmemory.h>
- #include<libxml/parser.h>
- /*解析storyinfo节点,打印keyword节点的内容*/
- voidparseStory(xmlDocPtrdoc,xmlNodePtrcur){
- xmlChar*key;
- cur=cur->xmlChildrenNode;
- while(cur!=NULL){
- /*找到keyword子节点*/
- if(!xmlStrcmp(cur->name,(constxmlChar*)"keyword")){
- key=xmlNodeListGetString(doc,cur->xmlChildrenNode,1);
- printf("keyword:%s\n",key);
- xmlFree(key);
- }
- cur=cur->next;/*下一个子节点*/
- }
- return;
- }
- /*解析文档*/
- staticvoidparseDoc(char*docname){
- /*定义文档和节点指针*/
- xmlDocPtrdoc;
- xmlNodePtrcur;
- /*进行解析,如果没成功,显示一个错误并停止*/
- doc=xmlParseFile(docname);
- if(doc==NULL){
- fprintf(stderr,"Documentnotparsesuccessfully.\n");
- return;
- }
- /*获取文档根节点,若无内容则释放文档树并返回*/
- cur=xmlDocGetRootElement(doc);
- if(cur==NULL){
- fprintf(stderr,"emptydocument\n");
- xmlFreeDoc(doc);
- return;
- }
- /*确定根节点名是否为story,不是则返回*/
- if(xmlStrcmp(cur->name,(constxmlChar*)"story")){
- fprintf(stderr,"documentofthewrongtype,rootnode!=story");
- xmlFreeDoc(doc);
- return;
- }
- /*遍历文档树*/
- cur=cur->xmlChildrenNode;
- while(cur!=NULL){
- /*找到storyinfo子节点*/
- if(!xmlStrcmp(cur->name,(constxmlChar*)"storyinfo")){
- parseStory(doc,cur);/*解析storyinfo子节点*/
- }
- cur=cur->next;/*下一个子节点*/
- }
- xmlFreeDoc(doc);/*释放文档树*/
- return;
- }
- intmain(intargc,char**argv){
- char*docname;
- if(argc<=1){
- printf("Usage:%sdocname\n",argv[0]);
- return0;
- }
- docname=argv[1];
- parseDoc(docname);
- return1;
- }
#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文档的基本流程如下:
(1)定义文档指针和节点指针。
(2)调用xmlParseFile()解析文档。如果不成功,注册一个错误并停止。一个常见错误是不适当的编码。XML标准文档除了用默认的UTF-8或UTF-16外,还可显式指定用其它编码保存。如果文档是这样,libxml2将自动地为你转换到UTF-8。更多关于XML编码信息包含在XML标准中。
(3)调用xmlDocGetRootElement()获取文档根节点,若无根节点则释放文档树并返回。
(4)确认文档是正确的类型,通过检查根节点名称来判断。
(5)检索节点的内容,这需要遍历文档树。对每个节点,遍历其子节点都需要一个循环。先用cur = cur->xmlChildrenNode获取第一个子节点,然后通过cur = cur->next不断向前遍历,直到cur==NULL。查找找指定节点时使用xmlStrcmp()函数,如果你指定的名称相同,就找到了你要的节点。通常把查找某个子节点的过程封装成函数。
(6)获取节点中的内容。查找到指定节点后,调用xmlNodeListGetString()获取节点下的文本。注意在XML中,包含在节点中的文本是这个节点的子节点,因此获取的是cur->xmlChildrenNode中的字符串。xmlNodeListGetString()会为返回的字符串分配内存,因此记得要用xmlFree()来释放它。
(7)调用xmlFreeDoc()释放文档树指针。
2、使用XPath查询信息
在xml文档中查询信息是一项核心工作。Libxml2支持使用XPath表达式来查找匹配的节点集。简而言之,XPath之于xml,好比SQL之于关系数据库。要在一个复杂的xml文档中查找所需的信息,XPath简直是必不可少的工具。下面代码查询所有keyword元素的内容。
- #include<libxml/parser.h>
- #include<libxml/xpath.h>
- /*解析文档*/
- xmlDocPtrgetdoc(char*docname){
- xmlDocPtrdoc;
- doc=xmlParseFile(docname);
- if(doc==NULL){
- fprintf(stderr,"Documentnotparsedsuccessfully.\n");
- returnNULL;
- }
- returndoc;
- }
- /*查询节点集*/
- xmlXPathObjectPtrgetnodeset(xmlDocPtrdoc,xmlChar*xpath){
- xmlXPathContextPtrcontext;
- xmlXPathObjectPtrresult;/*存储查询结果*/
- /*创建一个xpath上下文*/
- context=xmlXPathNewContext(doc);
- if(context==NULL){
- printf("ErrorinxmlXPathNewContext\n");
- returnNULL;
- }
- /*查询XPath表达式*/
- result=xmlXPathEvalExpression(xpath,context);
- xmlXPathFreeContext(context);/*释放上下文指针*/
- if(result==NULL){
- printf("ErrorinxmlXPathEvalExpression\n");
- returnNULL;
- }
- /*检查结果集是否为空*/
- if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
- xmlXPathFreeObject(result);/*如为这空就释放*/
- printf("Noresult\n");
- returnNULL;
- }
- returnresult;
- }
- intmain(intargc,char**argv){
- char*docname;
- xmlDocPtrdoc;
- /*查找所有keyword元素,而不管它们在文档中的位置*/
- xmlChar*xpath=(xmlChar*)"//keyword";
- xmlNodeSetPtrnodeset;
- xmlXPathObjectPtrresult;
- inti;
- xmlChar*keyword;
- if(argc<=1){
- printf("Usage:%sdocname\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);
- }
#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查询信息的基本流程如下:
(1)调用xmlXPathNewContext()给文档树创建一个上下文指针。
(2)调用xmlXPathEvalExpression(),传入XPath表达式和上下文指针,返回一个xmlXPathObjectPtr结果集指针。nodesetval对象包含keyword节点个数(nodeNr)和节点列表(nodeTab)。在使用之前要和xmlXPathNodeSetIsEmpty()检查nodesetval节点列表是否为空。
(3)遍历节点列表nodeTab,用xmlNodeListGetString()获取每个keyword节点的内容。
(4)用xmlXPathFreeObject()释放查询结果,用xmlFreeDoc()释放文档树。
更多关于Xpath的内容可以参考XPath官方规范http://www.w3.org/TR/xpath/。XPath语法的介绍,可参考w3school上的教程http://www.w3school.com.cn/xpath/index.asp,或者http://w3schools.com/xpath/default.asp。只有掌握XPath,才能掌握使用大型XML文件获取信息的方法,否则每寻找一个节点都要从根节点找起,很耗时耗力。
3、修改xml文档
这与上面的过程类似,首先遍历文档树,找到要插入(或删除)的节点处,然后插入(或删除)相关的内容。下面代码在storyinfo节点下插入一个keyword元素。
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #include<libxml/xmlmemory.h>
- #include<libxml/parser.h>
- void
- parseStory(xmlDocPtrdoc,xmlNodePtrcur,constxmlChar*keyword){
- /*在当前节点下插入一个keyword子节点*/
- xmlNewTextChild(cur,NULL,(constxmlChar*)"keyword",keyword);
- return;
- }
- xmlDocPtr
- parseDoc(char*docname,char*keyword){
- xmlDocPtrdoc;
- xmlNodePtrcur;
- doc=xmlParseFile(docname);
- if(doc==NULL){
- fprintf(stderr,"Documentnotparsedsuccessfully.\n");
- return(NULL);
- }
- cur=xmlDocGetRootElement(doc);
- if(cur==NULL){
- fprintf(stderr,"emptydocument\n");
- xmlFreeDoc(doc);
- return(NULL);
- }
- if(xmlStrcmp(cur->name,(constxmlChar*)"story")){
- fprintf(stderr,"documentofthewrongtype,rootnode!=story");
- xmlFreeDoc(doc);
- return(NULL);
- }
- cur=cur->xmlChildrenNode;
- while(cur!=NULL){
- if((!xmlStrcmp(cur->name,(constxmlChar*)"storyinfo"))){
- parseStory(doc,cur,(constxmlChar*)keyword);
- }
- cur=cur->next;
- }
- return(doc);
- }
- int
- main(intargc,char**argv){
- char*docname;
- char*keyword;
- xmlDocPtrdoc;
- if(argc<=2){
- printf("Usage:%sdocname,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);
- }
#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")){
- xmlNodePtrtempNode;
- tempNode=cur->next;
- xmlUnlinkNode(cur);
- xmlFreeNode(cur);
- cur=tempNode;
- continue;
- }
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){
- xmlDocPtrdoc;
- xmlNodePtrcur;
- xmlNodePtrnewnode;
- xmlAttrPtrnewattr;
- doc=xmlParseFile(docname);
- if(doc==NULL){
- fprintf(stderr,"Documentnotparsedsuccessfully.\n");
- return(NULL);
- }
- cur=xmlDocGetRootElement(doc);
- if(cur==NULL){
- fprintf(stderr,"emptydocument\n");
- xmlFreeDoc(doc);
- return(NULL);
- }
- if(xmlStrcmp(cur->name,(constxmlChar*)"story")){
- fprintf(stderr,"documentofthewrongtype,rootnode!=story");
- xmlFreeDoc(doc);
- return(NULL);
- }
- newnode=xmlNewTextChild(cur,NULL,"reference",NULL);
- newattr=xmlNewProp(newnode,"uri",uri);
- return(doc);
- }
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(xmlDocPtrdoc,xmlNodePtrcur){
- xmlChar*uri;
- cur=cur->xmlChildrenNode;
- while(cur!=NULL){
- if((!xmlStrcmp(cur->name,(constxmlChar*)"reference"))){
- uri=xmlGetProp(cur,"uri");
- printf("uri:%s\n",uri);
- xmlFree(uri);
- }
- cur=cur->next;
- }
- return;
- }
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为属性声明一个固定的或默认的值,则该函数也查找这些值。
4、创建xml文档
有了上面的基础,创建一个xml文档显得非常简单,就是一个不断插入节点的过程。其流程如下:
(1)用xmlNewDoc函数创建一个文档指针doc;
(2)用xmlNewNode函数创建一个节点指针root_node;
(3)用xmlDocSetRootElement将root_node设置为doc的根结点;
(4)用xmlAddChild()给root_node添加一系列的子节点,并设置子节点的内容和属性;
(5)用xmlSaveFile将xml文档存入文件;
(6)用xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。
下面代码创建一个xml文档:
- #include<stdio.h>
- #include<iostream>
- #include<libxml/parser.h>
- #include<libxml/tree.h>
- usingnamespacestd;
- intmain(intargc,char*argv[]){
- //定义文档和节点指针
- 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;
- }
#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>newNode1content</newNode1>
- <newNode2>newNode2content</newNode2>
- <newNode3>newNode3content</newNode3>
- <node2attribute="yes">NODECONTENT</node2>
- <son>
- <grandson>Thisisagrandsonnode</grandson>
- </son>
- </root>
<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将新节点加入上层节点。
5、编码转换
数据编码兼容性问题是很多开发人员都会遇到的一大难题,特别是在使用libxml时。libxml内部使用UTF-8格式存储和操作数据。你的应用程序数据如果使用其他格式的编码,例如ISO-8859-1编码,则在传给libxml之前必须转换成UTF-8格式。如果你的应用输出想用非UTF-8格式的编码,也需要进行转换。
Libxml2本身只支持把UTF-8, UTF-16和ISO-8859-1格式的外部数据转换成内部使用的UTF-8格式,以及处理完后输出成这些格式的数据。对其他的字符编码,需要使用libiconv(当然你也可以使用其他的国际化库,例如ICU)。当前libiconv支持150多种不同的字符编码,libiconv的实现尽量保证支持所有我们听过的编码格式。在使用libxml之前,一般是通过libiconv把数据先转换UTF-8格式。在使用libxml处理完之后,再通过libiconv把数据输出成你要的编码格式。
一个常见的错误是一份代码的不同部分的数据使用不同的编码格式。例如内部数据使用ISO-8859-1格式的应用程序,联合使用libxml,而它的内部数据格式为UTF-8。这样应用程序在运行不同的代码段时要不同地对待内部数据,这有可能导致解析数据出现错误。
例子1:使用Libxml内建的编码处理器
下面的例子创建一个简单的文档,添加从命令行得到的数据到文档根元素,并以合适的编码格式输出到stdout。对提供的数据我们使用ISO-8859-1编码,处理过程为从ISO-8859-1到UTF-8,再到ISO-8859-1。命令行上输入的字符串从ISO-8859-1格式转换成UTF-8格式,以供libxml使用,输出时又重新转换成ISO-8859-1格式。
- #include<string.h>
- #include<libxml/parser.h>
- /*对指定编码格式的外部数据,转换成libxml使用UTF-8格式*/
- unsignedchar*
- convert(unsignedchar*in,char*encoding){
- unsignedchar*out;
- intret,size,out_size,temp;
- /*定义一个编码处理器指针*/
- xmlCharEncodingHandlerPtrhandler;
- size=(int)strlen((constchar*)in)+1;/*输入数据长度*/
- out_size=size*2-1;/*输出数据长度*/
- out=(unsignedchar*)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("conversionwasn'tsuccessful.\n");
- }else{/*只转换了一部分数据*/
- printf("conversionwasn'tsuccessful.converted:%ioctets.\n",temp);
- }
- free(out);
- out=NULL;
- }else{/*转换成功*/
- out=(unsignedchar*)realloc(out,out_size+1);
- out[out_size]=0;/*输出的末尾加上null终止符*/
- }
- }else{
- printf("nomem\n");
- }
- return(out);
- }
- int
- main(intargc,char**argv){
- unsignedchar*content,*out;
- xmlDocPtrdoc;
- xmlNodePtrrootnode;
- char*encoding="ISO-8859-1";
- if(argc<=1){
- printf("Usage:%scontent\n",argv[0]);
- return(0);
- }
- content=(unsignedchar*)argv[1];
- /*转换成libxml2使用的UTF-8格式*/
- out=convert(content,encoding);
- doc=xmlNewDoc(BAD_CAST"1.0");
- rootnode=xmlNewDocNode(doc,NULL,(constxmlChar*)"root",out);
- xmlDocSetRootElement(doc,rootnode);
- /*以ISO-8859-1格式输出文档内容*/
- xmlSaveFormatFileEnc("-",doc,encoding,1);
- return(1);
- }
#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格式(我的系统中不是),则输出文档为:
- <?xmlversion="1.0"encoding="ISO-8859-1"?>
- <root>zhou</root>
<?xml version="1.0" encoding="ISO-8859-1"?> <root>zhou</root>编码转换的基本流程如下:
(1)用xmlCharEncodingHandlerPtr定义一个编码处理器指针,用xmlFindCharEncodingHandler()查找libxml2中指定的编码处理器。libxml2内建只支持把UTF-8, UTF-16和ISO-8859-1格式的外部数据转换成内部使用的UTF-8格式。如果要转换其他格式的数据(如中文编码),则要使用独立的libiconv库给libxml2注册新编码处理器。
(2)调用编码处理器的input()函数,把外部数据转换成libxml2使用的格式。
(3)进行xml处理,处理完若要保存成非UTF-8格式的文档,使用xmlSaveFormatFileEnc()函数。若保存的编码格式libxml2不支持,则只能用libiconv把保存的文档转换成需要的编码格式。
例子2:通过iconv库给Libxml注册新的编码处理器
下面例子先编写GBK的编码处理器gbk_input()和gbk_output(),前者是GBK到UTF-8输入处理,后者是UTF-8到GBK输出处理,这两个处理器都要用到iconv转换函数。然后调用xmlNewCharEncodingHandler()注册输入输出处理器。对输入输出数据的编码转换由convertToUTF8From()和utf8ConvertTo()来完成,它们都是调用xmlFindCharEncodingHandler()查找已注册的处理器,然后在处理器上调用input()或output()对数据进行编码转换。
- #include<string.h>
- #include<iconv.h>
- #include<libxml/encoding.h>
- #include<libxml/xmlwriter.h>
- #include<libxml/xmlreader.h>
- /*输入编码处理器:GBK到UTF-8*/
- intgbk_input(unsignedchar*out,int*outlen,
- constunsignedchar*in,int*inlen){
- char*outbuf=(char*)out;
- char*inbuf=(char*)in;
- iconv_ticonv_from;/*gbk到utf-8的转换描述符*/
- size_tlen1,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){
- returnrslt;
- }
- iconv_close(iconv_from);/*释放描述符*/
- *outlen=((unsignedchar*)outbuf-out);
- *inlen=((unsignedchar*)inbuf-in);
- return*outlen;
- }
- /*输出编码处理器:UTF-8到GBK*/
- intgbk_output(unsignedchar*out,int*outlen,
- constunsignedchar*in,int*inlen){
- char*outbuf=(char*)out;
- char*inbuf=(char*)in;
- iconv_ticonv_to;/*utf-8到gbk的转换描述符*/
- size_tlen1,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){
- returnrslt;
- }
- iconv_close(iconv_to);/*释放描述符*/
- *outlen=((unsignedchar*)outbuf-out);
- *inlen=((unsignedchar*)inbuf-in);
- return*outlen;
- }
- /**
- *convertToUTF8From:
- *把encoding编码的输入数据in转换成utf-8格式返回
- *出错则返回NULL
- */
- xmlChar*convertToUTF8From(constchar*in,constchar*encoding){
- xmlChar*out;
- intret;
- intsize;
- intout_size;
- inttemp;
- xmlCharEncodingHandlerPtrhandler;
- if(in==0)
- return0;
- /*查找内建的编码处理器*/
- handler=xmlFindCharEncodingHandler(encoding);
- if(!handler){
- printf("convertToUTF8From:noencodinghandlerfoundfor'%s'\n",
- encoding?encoding:"");
- return0;
- }
- size=(int)strlen(in)+1;/*输入数据长度*/
- out_size=size*2-1;/*输出数据长度*/
- /*存放输出数据*/
- out=(unsignedchar*)xmlMalloc((size_t)out_size);
- memset(out,0,out_size);
- if(out!=NULL){
- temp=size-1;
- /*对输入数据进行编码转换,成功后返回0*/
- ret=handler->input(out,&out_size,(constxmlChar*)in,&temp);
- if(ret||temp-size+1){/*转换不成功*/
- if(ret){/*转换失败*/
- printf("convertToUTF8From:conversionwasn'tsuccessful.\n");
- }else{/*只转换了一部分数据*/
- printf("convertToUTF8From:conversionwasn'tsuccessful.converted:%ioctets.\n",temp);
- }
- xmlFree(out);/*释放输出缓冲区*/
- out=0;
- }else{/*转换成功,在输出末尾加上null终止符*/
- out=(unsignedchar*)xmlRealloc(out,out_size+1);
- out[out_size]=0;
- }
- }else{
- printf("convertToUTF8From:nomem\n");
- }
- returnout;
- }
- /**
- *utf8ConvertTo:
- *把utf-8的数据转换成encoding编码返回
- *出错则返回NULL
- */
- char*utf8ConvertTo(xmlChar*in,constchar*encoding){
- char*out;
- intret;
- intsize;
- intout_size;
- inttemp;
- xmlCharEncodingHandlerPtrhandler;
- if(in==0)
- return0;
- handler=xmlFindCharEncodingHandler(encoding);
- if(!handler){
- printf("utf8ConvertTo:noencodinghandlerfoundfor'%s'\n",
- encoding?encoding:"");
- return0;
- }
- 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,(constxmlChar*)in,&temp);
- if(ret||temp-size+1){
- if(ret){
- printf("utf8ConvertTo:conversionwasn'tsuccessful.\n");
- }else{
- printf("utf8ConvertTo:conversionwasn'tsuccessful.converted:%ioctets.\n",temp);
- }
- free(out);
- out=0;
- }else{
- out=(char*)realloc(out,out_size+1);
- out[out_size]=0;/*末尾加上null终止符*/
- }
- }else{
- printf("utf8ConvertTo:nomem\n");
- }
- returnout;
- }
- intmain(intargc,char**argv){
- constchar*content;
- xmlChar*out;
- xmlDocPtrdoc;
- xmlNodePtrrootnode;
- if(argc<=1){
- printf("Usage:%scontent\n",argv[0]);
- return(0);
- }
- content=(constchar*)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,(constxmlChar*)"root",out);
- xmlDocSetRootElement(doc,rootnode);
- /*以gb2312格式保存文档内容:"-"表示输出到终端*/
- xmlSaveFormatFileEnc("-",doc,"gb2312",1);
- xmlCleanupCharEncodingHandlers();/*释放编码处理器资源*/
- return(1);
- }
#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库的转换资源。
例子3:直接使用iconv库进行转换
下面例子直接使用iconv函数对输入输出进行编码转换,而不是通过注册编码处理器的方式。
- #include<stdio.h>
- #include<string.h>
- #include<iconv.h>
- #include<libxml/parser.h>
- #include<libxml/tree.h>
- /*代码转换:从一种编码转为另一种编码*/
- intencoding_convert(constchar*from_charset,constchar*to_charset,
- char*inbuf,intinlen,
- char*outbuf,intoutlen){
- iconv_tcd;
- size_tlen1,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);/*释放描述符*/
- return0;
- }
- /*GB2312转换为UTF-8
- *成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL
- */
- char*gb2312_utf8(char*inbuf){
- intnOutLen=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;
- }
- returnszOut;
- }
- /*UTF-8转换为GB2312
- *成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL
- */
- char*utf8_gb2312(char*inbuf){
- intnOutLen=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;
- }
- returnszOut;
- }
- intmain(intargc,char**argv){
- /*定义文档节点和指针*/
- xmlDocPtrdoc=xmlNewDoc(BAD_CAST"1.0");
- xmlNodePtrroot_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"newNode1content");
- xmlNewTextChild(root_node,NULL,BAD_CAST"newNode2",BAD_CAST"newNode2content");
- xmlNewTextChild(root_node,NULL,BAD_CAST"newNode3",BAD_CAST"newNode3content");
- xmlNewChild(root_node,NULL,BAD_CAST"node1",BAD_CASTszOut);
- xmlFree(szOut);
- /*创建一个节点,设置其内容和属性,然后加入根结点*/
- xmlNodePtrnode=xmlNewNode(NULL,BAD_CAST"node2");
- xmlNodePtrcontent=xmlNewText(BAD_CAST"NODECONTENT");
- xmlAddChild(root_node,node);
- xmlAddChild(node,content);
- szOut=gb2312_utf8("属性值");
- xmlNewProp(node,BAD_CAST"attribute",BAD_CASTszOut);
- xmlFree(szOut);
- /*创建一个中文节点*/
- szOut=gb2312_utf8("中文节点");
- xmlNewChild(root_node,NULL,BAD_CASTszOut,BAD_CAST"contentofchinesenode");
- xmlFree(szOut);
- /*存储xml文档*/
- intnRel=xmlSaveFormatFileEnc("CreatedXml_cn.xml",doc,"GB2312",1);
- if(nRel!=-1){
- printf("一个xml文档被创建,写入%d个字节",nRel);
- }
- xmlFreeDoc(doc);
- return1;
- }
#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转换的方式更高效。编译并运行程序,输出文档如下:
- <?xmlversion="1.0"encoding="GB2312"?>
- <root>
- <newNode1>newNode1content</newNode1>
- <newNode2>newNode2content</newNode2>
- <newNode3>newNode3content</newNode3>
- <node1>节点1的内容</node1>
- <node2attribute="属性值">NODECONTENT</node2>
- <中文节点>contentofchinesenode</中文节点>
- </root>
<?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、一个真实的例子
内容整理自http://xmlsoft.org/example.html。
下面是一个真实的例子。应用程序数据的内容不使用DOM树,而是使用内部数据结构来保存。这是一个基于XML存储结构的数据库,它保存了与Gnome相关的任务。如下:
- <?xmlversion="1.0"?>
- <gjob:Helpingxmlns:gjob="http://www.gnome.org/some-location">
- <gjob:Jobs>
- <gjob:Job>
- <gjob:ProjectID="3"/>
- <gjob:Application>GBackup</gjob:Application>
- <gjob:Category>Development</gjob:Category>
- <gjob:Update>
- <gjob:Status>Open</gjob:Status>
- <gjob:Modified>Mon,07Jun199920:27:45-0400METDST</gjob:Modified>
- <gjob:Salary>USD0.00</gjob:Salary>
- </gjob:Update>
- <gjob:Developers>
- <gjob:Developer>
- </gjob:Developer>
- </gjob:Developers>
- <gjob:Contact>
- <gjob:Person>NathanClemons</gjob:Person>
- <gjob:Email>nathan@windsofstorm.net</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>
- Theprogramshouldbereleasedasfreesoftware,undertheGPL.
- </gjob:Requirements>
- <gjob:Skills>
- </gjob:Skills>
- <gjob:Details>
- AGNOMEbasedsystemthatwillallowasuperusertoconfigure
- compressedanduncompressedfilesand/orfilesystemstobebacked
- upwithasupportedmediainthesystem.Thisshouldbeableto
- performviafindcommandsgeneratingalistoffilesthatarepassed
- totar,dd,cpio,cp,gzip,etc.,tobedirectedtothetapemachine
- orviaoperationsperformedonthefilesystemitself.Email
- notificationandGUIstatusdisplayveryimportant.
- </gjob:Details>
- </gjob:Job>
- </gjob:Jobs>
- </gjob:Helping>
<?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>nathan@windsofstorm.net</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树中只是调用几个函数的问题,而遍历整个树来收集数据,并生成内部结构则更困难,也更容易出错。
对输入结构的定义法则是非常宽松的。属性的顺序无关紧要(XML规范清楚地说明了这一点),不要依赖于一个节点的子节点顺序通常是一个好的主意,除非这样做真的使事情变得更困难了。下面是解析person信息的一段代码:
- /*
- *一个person记录
- */
- typedefstructperson{
- char*name;
- char*email;
- char*company;
- char*organisation;
- char*smail;
- char*webPage;
- char*phone;
- }person,*personPtr;
- /*
- *解析person的代码
- */
- personPtrparsePerson(xmlDocPtrdoc,xmlNsPtrns,xmlNodePtrcur){
- personPtrret=NULL;
- DEBUG("parsePerson\n");
- /*
- *为结构分配内存
- */
- ret=(personPtr)malloc(sizeof(person));
- if(ret==NULL){
- fprintf(stderr,"outofmemory\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);
- }
/* * 一个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); }下面是要注意的一些事项:
(1)通常一个递归的解析风格是更方便的:XML数据天然地遵循重复式地构造,并且是高度结构化的。
(2)两个参数是xmlDocPtr和xmlNsPtr类型,即指向XML文档和应用程序保留的命名空间的指针。文档信息非常广泛,为你的应用程序数据集定义一个命名空间并测试元素和属性是否属性这个空间是一个好的编程实践。这只需一个简单的相等测试(cur->ns == ns)。
(3)为了查询文本和属性值,你可以使用函数xmlNodeListGetString()来获取所有文本,和由DOM输出生成的引用节点,并生成一个单一的文本字符串。
下面是解析另外一个结构的代码片段:
- #include<libxml/tree.h>
- /*
- *一个Job的描述
- */
- typedefstructjob{
- char*projectID;
- char*application;
- char*category;
- personPtrcontact;
- intnbDevelopers;
- personPtrdevelopers[100];/*usingdynamicallocisleftasanexercise*/
- }job,*jobPtr;
- /*
- *解析Job的代码
- */
- jobPtrparseJob(xmlDocPtrdoc,xmlNsPtrns,xmlNodePtrcur){
- jobPtrret=NULL;
- DEBUG("parseJob\n");
- /*
- *为结构分配内存
- */
- ret=(jobPtr)malloc(sizeof(job));
- if(ret==NULL){
- fprintf(stderr,"outofmemory\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,"ProjecthasnoID\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);
- }
#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文档。
相关推荐
"对libxml2封装的简单C++ xml操作类,libxml入门经典" 这个标题表明我们即将探讨的是一个基于C++的XML处理库——libxml2的使用,特别是针对初学者的一个经典教程。它强调的是通过封装libxml2库,创建一个易于使用的...
通过阅读和分析这个文件,开发者可以更好地理解和掌握 libxml2 的 API 使用。 5. **test.c**: 这是另一个测试文件,通常用于验证库的功能或者演示某个特定功能的用法。开发者可以通过这个文件了解如何在实际项目中...
NS3教程,高手绕道 NS3是网络模拟器,它可以模拟各种网络场景,例如无线网络、有线网络、移动网络等。NS3提供了一个灵活的平台,允许用户定义和模拟复杂的网络场景。下面是NS3的安装说明和简单的使用方法。 一、NS...
Cocos2dx入门初级教程的知识点详解 一、Cocos2dx简介 Cocos2dx是一个用于构建2D游戏、演示程序和图形/交互式应用程序的框架。它基于OpenGL进行开发,并且依赖于一系列的库,包括GLEW(OpenGL扩展库)、libxml2...
在Linux系统上,你可以使用`yum`命令安装这些基础组件: 1. 安装MySQL服务器和开发库,以支持Cacti的数据存储: ``` yum -y install mysql mysql-server mysql-devel ``` 2. 安装Apache Web服务器: ``` yum -...
### NS3对于初学者的教程 #### 一、NS3简介 NS3(Network Simulator 3)是一款开源的网络仿真平台,广泛应用于学术研究和工业界。它支持广泛的网络协议和应用模型,能够帮助研究人员和工程师们理解网络行为并评估新...
2. **安装依赖**:使用包管理器安装必要的依赖,例如在Ubuntu上: ``` sudo apt-get install build-essential perl libpam0g-dev libxml2-dev python-dev python-setuptools ``` 在CentOS/RHEL上: ``` sudo ...
14. **脚本自动化**:使用Perl编写系统管理脚本,例如监控、备份、日志分析等。 15. **Perl最佳实践**:学习编写清晰、可维护的代码,包括命名规范、注释、代码组织和测试。 通过这个24学时的教程,你将不仅能够...
本教程主要聚焦于数据解析,这是爬虫过程中至关重要的一个环节。数据解析是指将爬取到的原始网页数据转换为结构化的、有意义的信息。下面,我们将深入探讨Python中用于数据解析的一些关键技术和工具。 1. **...
- **LibXML 函数**:如 libxml_use_internal_errors(),用于处理XML文档。 - **Mail 函数**:如 mail(),用于发送邮件。 - **Math 函数**:如 sqrt(),用于数学运算。 - **Misc 函数**:如 getrandmax(),用于其他...
3. **依赖库**:确保系统上安装了所有必要的依赖库,例如libxml2、libcurl等。如果在编译过程中遇到错误,可能需要手动安装这些依赖。 **SIPP教程** SIPP的主要使用方式是通过命令行参数和自定义脚本来操作。以下...
2. **解压文件**:使用命令行工具(如`tar`或`7-zip`)解压下载的文件到适当目录,例如`/usr/local/src/`。 3. **进入源码目录**:使用`cd`命令进入解压后的源码目录,例如`cd gdal-1.10.0`。 4. **配置GDAL**:运行...
本教程使用的Python库为`libxml2dom`,这是一个强大的XML文档处理库,能够方便地读取、创建和修改XML文档。虽然HTML与XML格式有所差异,但`libxml2dom`也能够很好地解析HTML文档。 **安装步骤:** 确保已安装`...
3. 其他依赖库:包括OpenSSL、 readline、libxml2、zlib、ncurses等。 四、编译步骤 1. 解压源码:`tar -zxvf gpdb-5.8.0.tar.gz` 2. 进入源码目录:`cd gpdb-5.8.0` 3. 配置编译环境:根据实际系统环境执行相应的...
2. **内存优化**:使用迭代器或流式处理大文件,避免一次性加载整个文档到内存。 3. **性能优化**:了解并使用`lxml.objectify`模块,它提供了一种面向对象的API,适合处理大型XML数据。 4. **XSLT转换**:lxml支持...
这通常涉及正则表达式或DOM解析库的使用,如libxml2或TinyXML。 3. **文本分词**:对提取的文本进行分词,是将连续的字符流转换成有意义的词汇单元。分词器的设计需要考虑语言特点和停用词列表,常见的分词算法有...
3. `user-guide.chm`:用户指南,通常包含详细的操作步骤和使用教程。 4. `ws.css`:样式表文件,用于控制Wireshark用户界面的显示样式。 5. `libwireshark.dll`、`libgtk-win32-2.0-0.dll`:动态链接库文件,分别为...
4. **CSS选择器**:使用库提供的功能,根据CSS选择器定位到目标元素。例如, AngleSharp 提供了类似jQuery的API,方便我们像在浏览器中那样选择元素。 5. **JavaScript执行**:如果网页包含JavaScript,可能需要...
3. **lxml**:lxml是Python的一个高效库,它结合了libxml2和libxslt的功能,提供了对HTML和XML的高性能解析和处理。lxml不仅速度快,而且功能强大,支持XPath和CSS选择器,适用于处理大规模和复杂结构的网页数据。 ...