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");
???? 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("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源代碼如下:
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
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)
??? {
??????? fprintf(stderr,"empty document");
??????? xmlFreeDoc(doc);
??????? return -1;
??? }
??? //確認根元素名字是否符合
??? 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");
??????????? printf("get attribute=%s\n", szAttr) ;
??????????? xmlFree(szAttr);
??????? }
??????? attrPtr = attrPtr->next;
??? }
??? xmlFreeDoc(doc);
??? 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");
?????? return NULL;
??? }
??? if (xmlXPathNodeSetIsEmpty(result->nodesetval))?? //檢查查詢結果是否為空
??? {
?????? xmlXPathFreeObject(result);
?????? printf("nodeset is empty"n");
?????? return NULL;
??? }
??? 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)
?????? return -1;
??? memset(outbuf,0,outlen);??
??? if(iconv(cd,(const char**)pin,(unsigned int *)&inlen,pout,(unsigned int*)&outlen)
?????? == -1)
?????? return -1;??
??? iconv_close(cd);
??? return 0;??
}
//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*變量,需要在使用完畢后手動free,失敗返回NULL
char* g2u(char *inbuf)??
{
??? int nOutLen = 2 * strlen(inbuf) - 1;
??? char* szOut = (char*)malloc(nOutLen);
??? if (-1 == code_convert("gb2312","utf-8",inbuf,strlen(inbuf),szOut,nOutLen))
??? {
?????? free(szOut);
?????? szOut = NULL;
??? }
??? return szOut;
}
下面以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")