Libxml 是一個實現讀、創建及操縱XML數據功能的C語言庫。這個指南提供例子代碼并給出它基本功能的解釋。在這個項目的主頁上有Libxml及更多關于它可用的資料。包含有完整的API文檔。這個指南并不能替代這些完整的文檔,但是闡明功能需要使用庫來完成基本操作。
本指南中的例子代碼示范如何做到:
• 解析文檔
• 取得指定元素的文本
• 添加一個元素及它的內容
• 添加一個屬性
• 取得一個屬性的值
例子的完整代碼包含在附錄中
數據類型
Libxml 定義了許多數據類型,我們將反復碰到它們,它隱藏了雜亂的來源以致你不必處理它除非你有特定的需要。xmlChar 替代char,使用UTF-8編碼的一字節字符串。如果你的數據使用其它編碼,它必須被轉換到UTF-8才能使用libxml的函數。在libxml編碼支持WEB頁面有更多關于編碼的有用信息。
XmlDoc 包含由解析文檔建立的樹結構,xmlDocPtr是指向這個結構的指針。
xmlNodePtr and xmlNode 包含單一結點的結構xmlNodePtr是指向這個結構的指針,它被用于遍歷文檔樹。
解析文檔
解析文檔時僅僅需要文件名并只調用一個函數,并有錯誤檢查。完整代碼:附錄C, Keyword例程代碼
①xmlDocPtr doc;
②xmlNodePtr cur;
③doc = xmlParseFile(docname);
④if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. \n");
return;
}
⑤cur = xmlDocGetRootElement(doc);
⑥if (cur == NULL) {
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return;
}
⑦if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr,"document of the wrong type, root node != story");
xmlFreeDoc(doc);
return;
}
①定義解析文檔指針。
②定義結點指針(你需要它為了在各個結點間移動)。
④檢查解析文檔是否成功,如果不成功,libxml將指一個注冊的錯誤并停止。
注釋
一個常見錯誤是不適當的編碼。XML標準文檔除了用UTF-8或UTF-16外還可用其它編碼保存。如果文檔是這樣,libxml將自動地為你轉換到UTF-8。更多關于XML編碼信息包含在XML標準中。
⑤取得文檔根元素
⑥檢查確認當前文檔中包含內容。
⑦在這個例子中,我們需要確認文檔是正確的類型。“Story”是在這個指南中使用文檔的根類型。
取得元素內容
你找到在文檔樹中你要查找的元素后可以取得它的內容。在這個例子中我們查找“story”元素。進程將在冗長的樹中查找我們感興趣的元素。我們假定期你已經有了一個名為doc的xmlDocPtr和一個名為cur的xmlNodPtr。
①cur = cur->xmlChildrenNode;
②while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo"))){
parseStory (doc, cur);
}
cur = cur->next;
}
①取得cur的第一個子結點,cur指向文檔的根,即“story”元素。
②這個循環迭代通過“story”的子元素查找“storyinfo”。這是一個包含有我們將查找的“keywords”的元素。它使用了libxml字符串比較函數xmlStrcmp。如果相符,它調用函數parseStory。
void parseStory (xmlDocPtr doc, xmlNodePtr cur) {
xmlChar *key;
① cur = cur->xmlChildrenNode;
② while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"keyword"))) {
③ key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("keyword: %s\n", key);
xmlFree(key);
}
cur = cur->next;
}
return;
}
① 再次取得第一個子結點。
② 像上面那個循環一樣,我們能過迭代,查找我們感興趣的叫做“keyword”的元素。
③ 當我們找到元素“keyword”時,我們需要打印它包含在XML中的記錄的內容,文本被包含于元素的子結點中,因此我們借助了cur-> xmlChildrenNode,為了取得文本,我們使用函數xmlNodeListGetString,它有一個文檔指針參數,在這個例子中,我們僅僅打印它。
注釋
因為xmlNodeListGetString為它返回的字符串分配內存,你必須使用xmlFree釋放它。
使用XPath取得元素內容
除了一步步遍歷文檔樹查找元素外,Libxml2包含支持使用Xpath表達式取得指定結點集。完整的Xpath API文檔在這里。Xpath允許通過路徑文檔搜索匹配指定條件的結點。在下面的例子中,我們搜索文檔中所有的“keyword”元素。
注釋
下面是Xpath完整的討論。它詳細的使用資料,請查閱Xpath規范。
這個例子完整的代碼參見附錄D,XPath例程代碼。
Using XPath requires setting up an xmlXPathContext and then supplying the XPath expression and the context to the xmlXPathEvalExpression
function.
The function returns an xmlXPathObjectPtr, which includes the set of nodes satisfying the XPath expression.
使用XPath需要安裝xmlXPathContext才支持XPath表達式及xmlXPathEvalExpression函數,這個函數返回一個xmlXPathObjectPtr,它包含有
XPath表達式的結點集。
xmlXPathObjectPtr
getnodeset (xmlDocPtr doc, xmlChar *xpath){
①xmlXPathContextPtr context;
xmlXPathObjectPtr result;
②context = xmlXPathNewContext(doc);
③result = xmlXPathEvalExpression(xpath, context);
④if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
printf("No result\n");
return NULL;
}
xmlXPathFreeContext(context);
return result;
}
①首先定義變量
②初始化變量context
③應用XPath表達式
④檢查結果
由函數返回的xmlPathObjectPtr包含一個結點集和其它需要被迭代及操作的信息。在這個例子中我們的函數返回 xmlXPathObjectPtr,我們使用它打印我們文檔中keyword結點的內容。這個結點集對象包含在集合(nodeNr)中的元素數目及一個結點(nodeTab)數組。
①for (i=0; i < nodeset->nodeNr; i++) {
②keyword = xmlNodeListGetString(doc,
nodeset->nodeTab[i]->xmlChildrenNode, printf("keyword: %s\n", keyword);
xmlFree(keyword);
}
①變量nodeset->Nr持有結點集中元素的數量。我們使用它遍歷數組。
②打印每個結點包含的內容。
注釋
Note that we are printing the child node of the node that is returned, because the contents of the keyword element are a child text node.注意我們打印的是結點的子結點的返回值,因為keyword元素的內容是一個子文本結點。
寫元素
寫元素內容使用上面許多一樣的步驟—解析文檔并遍歷樹。我們先解析文檔然后遍歷樹查找我們想插入元素的位置。在這個例子中,我們再一次查找“storyinfo
”元素并插入一個keyword。然后我們裝文件寫入磁盤。完整代碼:附錄E,添加keyword例程
本例中主要的不同在于parseStory
void
parseStory (xmlDocPtr doc, xmlNodePtr cur, char *keyword) {
①xmlNewTextChild (cur, NULL, "keyword", keyword);
return;
}
①XmlNewTextChild函數添加一個當前結點的新的子元素到樹中
一旦結點被添加,我們應當寫文檔到文件中。你是否想給元素指定一個命名空間?你能添加它,在我們的例子中,命名空間是NULL。
xmlSaveFormatFile (docname, doc, 1);
第一個參數是寫入文件的名,你注意到和我們剛剛讀入的文件名是一樣的。在這個例子中,我們僅僅覆蓋原來的文件。第二個參數是一個xmlDoc結構指針,第三個參數設定為1,保證在輸出上寫入。