XML常见操作
1. 创建XML文档(1)创建一个XML文档非常简单,其流程如下: ① 用xmlNewDoc函数创建一个文档指针doc。 ② 用xmlNewNode函数创建一个节点指针root_node。 ③ 用xmlDocSetRootElement将root_node设置为doc的根结点。 ④ 给root_node添加一系列的子节点,并设置子节点的内容和属性。 ⑤ 用xmlSaveFile将XML文档存入文件。 ⑥ 用xmlFreeDoc关闭文档指针,并清除本文档中所有节点动态申请的内存。 有多种方式可以添加子节点,如可以用xmlNewTextChild直接添加一个文本子节点。也可以先创建新节点,然后用xmlAddChild将新节点加入到上层节点中。
(2)创建xml文件举例 CreateXmlFile.c源代码如下: #include <stdio.h> #include <libxml/parser.h> #include <libxml/tree.h> 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");
//创建一个节点,设置其内容和属性,然后加入根结点 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"); xmlNodePtr grandson = xmlNewNode(NULL,BAD_CAST "grandson"); xmlAddChild(grandson,xmlNewText(BAD_CAST "This is a grandson node")); //存储xml文档 int nRel = xmlSaveFile("CreateXml.xml",doc); if (nRel != -1) { printf("一个xml文档被创建,写入%d个字节n",nRel); } //释放文档内节点动态申请的内存 xmlFreeDoc(doc); return 1; } 编译 gcc CreateXmlFile.c -o CreateXmlFile -I/usr/local/include/libxml2 -lxml2。 执行./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> 最好使用类似XMLSPY这样的工具打开,因为这些工具可以自动整理XML文件的栅格,否则很有可能是没有任何换行的一个XML文件,可读性较差。 2. 解析XML文档(1)XML解析流程 解析一个XML文档,从中取出想要的信息,例如节点中包含的文字,或者某个节点的属性。其流程如下: ① 用xmlReadFile函数读入一个文件,并返回一个文档指针doc。 ② 用xmlDocGetRootElement函数得到根节点curNode。 ③ 此时curNode->xmlChildrenNode就是根节点的首个儿子节点,该儿子节点的兄弟节点可用next指针进行轮询。 ④ 轮询所有子节点,找到所需的节点,用xmlNodeGetContent取出其内容。 ⑤ 用xmlHasProp查找含有某个属性的节点,属性列表指针xmlAttrPtr将指向该节点的属性列表。 ⑥ 取出该节点的属性,用xmlGetProp取出其属性值。 ⑦ xmlFreeDoc函数关闭文档指针,并清除本文档中所有节点动态申请的内存。 (2)XML解析举例 ParseXmlFile.c源代码如下: int main(int argc,char* argv[]) xmlDocPtr doc; //定义解析文件指针 xmlNodePtr curNode; //定义结点指针 xmlChar *szKey; //临时字符串变量 char *szDocName; if (argc <= 1) printf("Usage: %s docname",argv[0]); return(0); szDocName = argv[1]; doc = xmlReadFile(szDocName,"GB2312",XML_PARSE_RECOVER); //解析文件 //检查解析文档是否成功,如果不成功,libxml将报错并停止解析。 //一个常见错误是不适当的编码,XML标准文档除了用UTF-8或UTF-16外还可用其它编码保存 if (NULL == doc) fprintf(stderr,"Document not parsed successfully."); return -1; //获取根节点 curNode = xmlDocGetRootElement(doc); if (NULL == curNode)
//确认根元素名字是否符合 if (xmlStrcmp(curNode->name,BAD_CAST "root"))
curNode = curNode->xmlChildrenNode; xmlNodePtr propNodePtr = curNode; while(curNode != NULL) //取出节点中的内容 if ((!xmlStrcmp(curNode->name,(const xmlChar *) "newNode1"))) szKey = xmlNodeGetContent(curNode); printf("newNode1: %sn",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,51); font-family:Arial; font-size:14px; line-height:26px"> xmlChar* szAttr = xmlGetProp(propNodePtr,BAD_CAST "attribute"); printf("get attribute=%sn",szAttr) ; xmlFree(szAttr); attrPtr = attrPtr->next; return 0; 编译 gcc ParseXmlFile.c -o ParseXmlFile -I/usr/local/include/libxml2 -lxml2。 执行 ./ParseXmlFile CreateXml.xml,执行结果如下: newNode1: newNode1 content get attribute=yes 3. 修改XML文档有了上面的基础,修改XML文档的内容就简单了。首先打开一个已经存在的XML文档,顺着根结点找到需要添加、删除、修改的地方,调用相应的XML函数对节点进行增、删、改操作。 需要注意的是,并没有xmlDelNode或者xmlRemoveNode函数,删除节点需使用以下一段代码: if (!xmlStrcmp(curNode->name,BAD_CAST "newNode1")) xmlNodePtr tempNode; tempNode = curNode->next; xmlUnlinkNode(curNode); xmlFreeNode(curNode); curNode = tempNode; continue; } 此段代码完成将当前节点从文档中断链(unlink),这样此XML文档就不会再包含这个节点,该节点断链后需使用xmlFreeNode来释放该节点申请的动态内存空间。 4. 使用XPath查找XML文档在libxml2中使用XPath非常简单,其流程如下: ① 定义一个XPath上下文指针xmlXPathContextPtr context,并且使用xmlXPathNewContext函数来初始化这个指针。 ② 定义一个XPath对象指针xmlXPathObjectPtr result,并且使用xmlXPathEvalExpression函数来计算XPath表达式,得到查询结果,将结果存入对象指针中。 ③ 使用result->nodesetval得到节点集合指针,其中包含了所有符合XPath查询结果的节点。 ④ 使用xmlXPathFreeContext释放上下文指针。 ⑤ 使用xmlXPathFreeObject释放XPath对象指针。 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"); if (xmlXPathNodeSetIsEmpty(result->nodesetval)) //检查查询结果是否为空 xmlXPathFreeObject(result); printf("nodeset is empty"n"); return result; } 5. 用iconv解决XML中字符集问题libxml2中默认的内码是UTF-8,所有使用libxml2进行处理的xml文件,必须首先显式或者默认转换为UTF-8编码才能被处理。 要在XML中使用中文,就必须能够在UTF-8和GB2312之间进行转换。libxml2提供了默认的内码转换机制,并且在libxml2的Tutorial中有一个例子,事实证明这个例子并不很适合用来转换中文。 有些场合需要使用iconv来进行编码转换,libxml2本身也是使用iconv进行编码转换的。iconv是一个专门用来进行编码转换的库,基本上支持目前所有常用的编码,它是glibc库的一个部分。 本节其实和libxml没有太大关系,可以把它简单看作是一个编码转换方面的专题。下文提供了一个通用转码函数,并在此基础上实现了两个转码封装函数,即从UTF-8转换到GB2312的函数u2g,以及反向转换的函数g2u。其代码如下: #include <iconv.h> #include <string.h> //代码转换,从一种编码转为另一种编码 int code_convert(char* from_charset,char* to_charset,char* inbuf, int inlen,char* outbuf,int outlen) iconv_t cd; char** pin = &inbuf; char** pout = &outbuf; cd = iconv_open(to_charset,from_charset); if(cd == 0) memset(outbuf,outlen); if(iconv(cd,(const char**)pin,(unsigned int *)&inlen,pout,(unsigned int*)&outlen) == -1) iconv_close(cd); //UNICODE码转为GB2312码 //成功则返回一个动态分配的char*变量,需要在使用完毕后手动free,失败返回NULL char* u2g(char *inbuf) int nOutLen = 2 * strlen(inbuf) - 1; char* szOut = (char*)malloc(nOutLen); if (-1 == code_convert("utf-8","gb2312",inbuf,strlen(inbuf),szOut,nOutLen)) free(szOut); szOut = NULL; return szOut; //GB2312码转为UNICODE码 char* g2u(char *inbuf) if (-1 == code_convert("gb2312","utf-8",51); font-family:Arial; font-size:14px; line-height:26px"> 下面以UTF-8到GB2312转码流程说明上文中转码函数的使用,使用流程如下: ① 得到一个UTF-8的字符串szSrc。 ② 定义一个char *的字符指针szDes,并不需要给它动态申请内存。 ③ 调用szDes = u2g(szSrc),这样szDes就指向转换后GB2312编码的字符串。 ④ 使用完这个字符串后使用free(szDes)来释放内存。 如果转码文件可以选择系统调用来进行文件转码。下文中f表示from,t表示to,其转码方法如下: system("iconv –f 源格式 –t 目标格式 源文件 >目标文件") system("iconv –f GB18030 –t UTF-8 test_gb.txt > test_utf.txt") 摘录自《深入浅出Linux工具与编程》 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |