使用XML技術可以友善的完成資料檔案的存儲及解析讀取,其格式化、解析過程由XML引擎完成,在Windows平台上可使用MSXML引擎,在Linux環境下可使用libxml2庫完成操作。本文簡要整理了在Linux環境下使用libxml2進行XML操作的C語言程式設計方式,包括檔案建立、讀寫操作。
Libxml2庫提供DOM、SAX操作接口,也實作了DTD、Scheme方式的驗證,支援XPath文法查詢,加上其穩定性及可移植性,滿足了一般項目的要求,有關Libxml2具體介紹可參考【1】。
1. 環境初始化
使用Libxml2庫,應確定在編譯環境中(尤其對于客戶機編譯安裝的分發軟體)需要相關頭檔案及連結庫,則可以通過Libxml2預定義宏确認:
#include
#include
#include
#include
#include
#include
#include
#if !(defined(LIBXML_WRITER_ENABLED) && defined(LIBXML_OUTPUT_ENABLED) / && defined LIBXML_READER_ENABLED) #error "cannot use xml lib" #endif
使用Libxml2進行XML檔案解析,則初始化解析環境,使其配置設定相應資源、設定變量。在實驗中,省略該步驟并未影響操作結果,但在記憶體檢查中會出現警告,可能導緻未預期結果。
int prj_xml_init()
{
xmlInitParser();
LIBXML_TEST_VERSION
return 0;
}
2. 寫入XML檔案
XML檔案為樹形組織結構,以惟一根元素開始,層次式記錄各屬性、元素等資訊,例如下檔案為描述一目錄樹結構:
Libxml2使用XML檔案指針操作XML檔案,使用XML節點指針操作節點,可使用xmlNewDoc()和xmlDocSetRootElement()建立,使用xmlSaveFileEnc儲存,例如:
#define PRJ_XML_ENCODING "ISO-8859-1" /* 編碼方式 */
#define PRJ_XML_FILEPATH "/home/prj/prjconf.xml" /* 檔案路徑 */
#define PRJ_XML_ROOT "FOLDER_ROOT" /* 根節點标簽 */
static xmlDocPtr pxmldoc = NULL; /*** XML資料檔案文檔執行個體 ***/
static xmlNodePtr pxmlroot = NULL; /*** XML資料檔案根節點 ***/
int prj_xml_writefile()
{
/*建立檔案 */
pxmldoc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
if (NULL == pxmldoc)
{
return -1;
}
/* 構造xml檔案句柄 */
pxmlroot = xmlNewDocNode(pxmldoc, NULL, BAD_CAST PRJ_XML_ROOT, NULL);
if (NULL == pxmlroot)
{
/* 失敗時清理資源 */
xmlFreeDoc(pxmldoc);
pxmldoc = NULL;
return -1;
}
/* 構造xml根節點 */
(void)xmlDocSetRootElement(pxmldoc, pxmlroot);
/* 儲存新XML資料檔案 */
(void)xmlSaveFileEnc(PRJ_XML_PATH, pxmldoc, PRJ_XML_ENCODING);
}
3. 寫入節點資訊
在XML檔案樹中建立子節點并寫入節點資訊,需要指明該節點的标簽及父節點,使用xmlNewChild()建立;增加、修改屬性資訊使用xmlNewProp()和xmlSetProp(),例如:
#define PRJ_XML_FOLDER " FOLDER" /* Folder節點标簽 */
#define PRJ_XML_FILE "FILE" /* File節點标簽 */
int prj_xml_newfolder() /* 增加Folder節點 */
{
xmlNode *pnode = NULL;
pnode = xmlNewChild(pxmlroot,NULL, BAD_CAST PRJ_XML_FOLDER, NULL);
if (NULL == pnode)
{
return NULL;
}
/* 增加屬性 */
(void)xmlNewProp(pnode, BAD_CAST "name", BAD_CAST "");
(void)xmlNewProp(pnode, BAD_CAST "attrib", BAD_CAST "");
return pnode;
}
int prj_xml_setprop(xmlNode *pnode, const char *attrib, const char *value) /* 修改屬性 */
{
return xmlSetProp(pnode, BAD_CAST attrib, BAD_CAST value);
}
4. 讀取節點資訊
Libxml2使用一個樹型結構組織XML檔案的全部資訊,包括節點的子節點、屬性等,則可以通過直接使用節點指針讀取資訊,但不是個通用方式。使用xmlReader和xmlWriter子產品可完成大量讀寫操作,可參考【2】。
對于基本的的讀取,可使用xmlGetProp()完成,它傳回一個xmlChar類型的字元數組包含屬性資訊,在使用完成後手動釋放資源,例如:
int prj_xml_readprop(xmlNode *pnode, const char *attrib) /* 讀取屬性 */
{
xmlChar *strres = NULL;
strres = xmlGetProp(pnode, BAD_CAST attrib);
if (NULL == strres)
{
return -1;
}
/* 其他操作 */
/* 清理資源 */
xmlFree(strres);
return 0;
}
5. 使用XPath查詢節點集
使用XPath文法可以表示特定的節點集,執行XPath語句則完成了查找特定節點集的功能。基本流程為:生成XPath查詢字元串、初始化XPath查詢環境、執行查詢獲得結果集、操作結果集、清理XPath查詢環境。
XPath文法有豐富的路徑表達方式、運算符及運算函數,可以完成複雜的節點查詢。例如:
查詢某Folder節點pnode下的所有檔案名以”File”開頭的檔案節點。使用GetNodePath()得到pnode的路徑字元串,使用start-with函數查詢。
char expr[64]={0}; /* 存放XPath語句 */
xmlChar *npath;
npath = xmlGetNodePath(pnode); /* 得到pnode的路徑字元串 */
(void)snprintf(expr, 64, "%s/%s[starts-with(@path,'%s/')]",
(const char *)npath, PRJ_XML_FILE, "File");
xmlFree(npath);
又如查詢名為”Folder2”的檔案夾,但在Windows上名稱對大小寫不敏感,則可以使用lower-case()函數将節點屬性轉為小寫再比較。對于Libxml2,暫未支援這個在xml2.0定義的函數,是以需要translat()函數代替,自然有性能的消耗:
FOLDER_ROOT/FOLDER[translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='Folder2']
生成XPath語句後,則可進行查詢得到查詢結果集,集合可能含有0、1或多個元素,然後進行具體操作:
int prj_xml_xpath_evaluate(const xmlChar* expr, xmlNodePtr *ppnode)
{
xmlXPathContextPtr pctx = NULL;
xmlXPathObjectPtr pobj = NULL;
if (NULL == expr)
{
return -1;
}
pctx = xmlXPathNewContext(pxmldoc); /* 初始化XPath查詢環境 */
if (NULL == pctx)
{
return -1;
}
pobj = xmlXPathEvalExpression(BAD_CAST expr,pctx); /* 執行查詢 */
if (NULL == pobj)
{
xmlXPathFreeContext(pctx);
return -1;
}
if (0 == pobj->nodesetval->nodeNr) /* 結果集為空 */
{
/* 其他操作 */
}
else
{
/* 其他操作 */
}
xmlXPathFreeObject(pobj); /* 清理XPath查詢環境 */
xmlXPathFreeContext(pctx);
return 0;
}
6. 環境清理
在完成XML檔案操作後,需進行資源清理。清理前保證修改資訊寫入檔案,可使用上文提到xmlSaveFileEnc()完成。清理環境操作如:
int32 prj_xml_clear()
{
if (NULL != pxmldoc)
{
/* 清理文檔連結清單資源 */
xmlFreeDoc(pxmldoc);
pxmldoc = NULL;
pxmlroot = NULL;
}
/* 通知xml解析結束 */
xmlCleanupParser();
return 0;
}
以上是在Linux環境是使用Libxml2庫完成XML操作的基本方式,完成複雜、具體需求的操作也可以通過參考資源資訊進行實驗完成。
Stone&Ice
From: http://blog.csdn.net/stoneandice
參考資源:
1. http://xmlsoft.org/ Libxml2官方網站
2. http://www.w3school.com.cn/x.asp W3school的XML參考手冊