[libxml2]_[XML處理]_[使用libxml2的xpath特性修改xml檔案內容]
阿新 • • 發佈:2019-01-27
場景:
1.在軟體需要儲存一些配置項時,使用資料庫的話比較複雜,檢視內容也不容易.純文字檔案對utf8字元支援也不好.
2.這時候使用xml是最佳選擇,使用跨平臺庫libxml2。
3.基於xpath的儲存方式對儲存區域性內容非常方便。
4.參考例子xpath2.c
5.實際耗時: 2小時.
檔案1: Makefile
CP="cp" build-post: test.exe ${CP} E:/software/Lib/file/xml-libxml2-2.7.1/win32/release/share/libxml2-2.dll . test.exe:test.o g++ -o test.exe test.o -LE:/software/Lib/file/xml-libxml2-2.7.1/win32/release/share -lxml2 test.o:test.cpp g++ -IE:/software/Lib/file/xml-libxml2-2.7.1/win32/release/share/include -c test.cpp -o test.o
檔案2:test.cpp
#include <stdio.h> #include <assert.h> #include <string> #include <iostream> #include <map> #include "libxml/tree.h" #include "libxml/parser.h" #include "libxml/xpath.h" #include "libxml/xpathInternals.h" #include "libxml/xmlsave.h" using namespace std; static void _UpdateXpathNodes(xmlNodeSetPtr nodes, const xmlChar* value) { int size; int i; assert(value); size = (nodes) ? nodes->nodeNr : 0; for(i = size - 1; i >= 0; i--) { assert(nodes->nodeTab[i]); xmlNodeSetContent(nodes->nodeTab[i], value); if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL) { nodes->nodeTab[i] = NULL; } } } static int _UpdateWithXpath(xmlXPathContextPtr xpathCtx,const char* key,const char* value) { xmlXPathObjectPtr xpathObj; xpathObj = xmlXPathEvalExpression(BAD_CAST key, xpathCtx); if(!xpathObj) { fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", key); return -1; } _UpdateXpathNodes(xpathObj->nodesetval, BAD_CAST value); xmlXPathFreeObject(xpathObj); return 0; } static int _UpdateXml(const char* path,map<string,string>& keyValue) { xmlDocPtr doc; xmlXPathContextPtr xpathCtx; doc = xmlParseFile(path); if (!doc) { fprintf(stderr, "Error: unable to parse file \"%s\"\n", path); return(-1); } /* Create xpath evaluation context */ xpathCtx = xmlXPathNewContext(doc); //1.注意,這裡根Node有宣告xmlns,那麼必須加下邊這句,相應的xpath要加字首 /c:container/c:rootfiles //xmlXPathRegisterNs(xpathCtx,BAD_CAST"c",BAD_CAST"urn:oasis:names:tc:opendocument:xmlns:container"); if(!xpathCtx) { fprintf(stderr,"Error: unable to create new XPath context\n"); xmlFreeDoc(doc); return(-1); } //3.update map<string,string>::iterator iter; map<string,string>::iterator end = keyValue.end(); for(iter = keyValue.begin();iter!= end;iter++) { cout << "word: " << iter->first << ", count: " << iter->second << endl; _UpdateWithXpath(xpathCtx,iter->first.c_str(),iter->second.c_str()); } xmlXPathFreeContext(xpathCtx); //4.save xmlSaveCtxtPtr saveCtxtPtx = xmlSaveToFilename(path,"UTF-8",XML_SAVE_FORMAT); if(!saveCtxtPtx) { xmlFreeDoc(doc); return -1; } if(-1 == xmlSaveDoc(saveCtxtPtx,doc)) { xmlFreeDoc(doc); return -1; } xmlSaveClose(saveCtxtPtx); //xmlDocDump(stdout, doc); //5.free xmlFreeDoc(doc); return 0; } int UpdateXml(const char* path,map<string,string>& keyValue) { /* Init libxml */ xmlInitParser(); int res = _UpdateXml(path,keyValue); xmlCleanupParser(); return res; } int main(int argc, char *argv[]) { printf("Hello, world\n"); map<string,string> m; m["/doc/parent/discarded/@info"] = string("info attri"); m["/doc/parent/discarded[2]"] = string("change second discarded text 中文"); int ret = UpdateXml("xpath2.res",m); assert(!ret); ret = UpdateXml("xpath2.res",m); assert(!ret); ret = UpdateXml("xpath2.res",m); assert(!ret); return 0; }
檔案3: xpath2.res
<?xml version="1.0" encoding="UTF-8"?> <doc> <parent> <discarded info="test">discarded</discarded> <preserved/> This text node must be discarded <discarded>test</discarded> <preserved> content1 <child1/> <child2>content2</child2> <preserved>too</preserved> <child2>content3</child2> <preserved/> <child2>content4</child2> <preserved/> <child2>content5</child2> content6 </preserved> </parent> </doc>
檔案4:修改後的 xpath2.res
<?xml version="1.0" encoding="UTF-8"?>
<doc>
<parent>
<discarded info="info attri">discarded</discarded>
<preserved/>
This text node must be discarded
<discarded>change second discarded text 中文</discarded>
<preserved>
content1
<child1/>
<child2>content2</child2>
<preserved>too</preserved>
<child2>content3</child2>
<preserved/>
<child2>content4</child2>
<preserved/>
<child2>content5</child2>
content6
</preserved>
</parent>
</doc>