1. 程式人生 > >[libxml2]_[XML處理]_[使用libxml2的xpath特性修改xml檔案內容]

[libxml2]_[XML處理]_[使用libxml2的xpath特性修改xml檔案內容]


場景:

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>