Windows环境下使用libxml2

最近有个需求,很简单,就是从一个XML文件中,删除掉一个节点。 根据以前的经验,轻车熟路的编译好libxml2库,写好xpath语句,执行程序,然后懵逼了……

const char* cszXPath = "/document//picture";  
pstXPathResult = xmlXPathEvalExpression(BAD_CAST cszXPath, pstCTX);

返回的竟然是空白结果,也就是说,没有找到节点。 经过反复确认,/document/item/richtext/par/picture这个节点确实存在在,XPath语句/document//picture也没有错误。一时之间,感觉无法下手,然后网上一查,发现了突破点!
网上大量的值资料都显示,在节点具备显式的命名空间时,如果用XPath检索,那么需要显式的表明命名空间,回头再看看xml文件:

<document xmlns="http://www.lotus.com/dxl" version="8.5" maintenanceversion="3.0" replicaid="492581A600324B45" form="MainTopic">

嗯,大写的xmlns,没跑了,就是它!
接下来上网再找找,看看怎么检索带命名空间的节点。
找了一大顿,结果比较有用的只有这样一段话。

使用libxml2的xpath进行节点查询时。在测试中十分正常。后来在应用中发现总是无法找到结果。返回结果数为空。
对比测试文件与系统生成xml文件发现关于xmlns属性变化引起异常。
当使用无prefix命名空间时,会引起此异常xmlns="http://www..com.cn/xxx.xsd"
百度N个网页后找到替代方案"//*[local-name()='UserTag'][@name=\'%s\']"
即在//*[local-name()='UserTag']中UserTag为节点名称@name=\'%s\'为属性name值

老实说,我道行浅薄,到现在也没看懂啥意思,倒是咬住了另一个关键字:perfix。然后我又上网找到了这么一段示例代码,贴到源程序里,看看效果:

const char* cszXPath = "/a:document//picture";  
pstXPathResult = xmlXPathEvalExpression(BAD_CAST cszXPath, pstCTX);

执行,看到了一条libxml库输出的错误消息:

XPath error : Undefined namespace prefix

出现了prefix,这个好眼熟啊,错误提示的内容是说prefix没有定义,嗯,我好像摸到点儿门路了……

然后接下来我找到了libxml2中的这个API!

XMLPUBFUN int XMLCALL xmlXPathRegisterNs (xmlXPathContextPtr ctxt,
						 const xmlChar *prefix,
						 const xmlChar *ns_uri);

我终于明白了,全明白了。

整理一下,答案应该是在这样的,libxml2正在使用xpath之前,需要创建context对象,如果节点带有命名空间(xmlns属性)那么在搜索前,就要向context对象指定命名空间,每一个命名空间对应XPath中的一个prefix。

所以,前文所述的问题,解决方案如下。

/* 示例文件 */
const char* cszFile = "../Simple.xml";
/* DOM对象 */
xmlDocPtr pstDOM;
/* Context对象 */
xmlXPathContextPtr pstContext;
/* XPath语句 */
const char* cszXPath = "/nodesns:document//nodesns:picture";
/* XPath搜索结果 */
xmlXPathObjectPtr pstXPathResult;

/* 打开文件 */
pstDOM = xmlParseFile(cszFileName);
/* 创建Context对象 */
pstContext = xmlXPathNewContext(pstDOM);
/* 重点1:向Context对象中注册namespace并声明perfix。*/
/* 第二个参数的“nodesns”就是perfix,它出现在XPath语句里,第三个参数中的字符串就是xml文件中,xmlns的真值。 */
/* 这样,XML中的namespace和XPath中的perfix的联系就建立起来了。 */
xmlXPathRegisterNs(pstContext, BAD_CAST "nodesns", BAD_CAST "http://www.lotus.com/dxl");
/* 搜索节点... */
pstXPathResult = xmlXPathEvalExpression(BAD_CAST cszXPath, pstContext);
/* 对搜索结果的处理,略。 */
......
/* 销毁搜索结果,释放资源 */
xmlXPathFreeObject(pstXPathResult);
/* 重点2:清理注册的perfix */
/* 由于一个xml中可能存在多个namespace,本次在一个namespace中搜索,下一次可能需要在另一个namespace中搜索, */
/* 所以,多次搜索动作如果想要正在同一个Context对象中完成的话,理论上,每次搜索完成后都要清理注册的perfix。 */
/* 新的搜索开始前重新注册新的perfix。 */
xmlXPathRegisteredNsCleanup(pstContext);
/* 销毁对象释放资源关闭文件不解释... */
xmlXPathFreeContext(pstContext);
xmlFreeDoc(pstDOM);

至此,问题解决。

转载于:https://my.oschina.net/Polarix/blog/2989684

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值