看了libxml2官方网站上关于xpath的 实例,还是不能很好的了解libxml是如何解析xpath并返回结果的。因为这些例子全部都是获取节点的名称,而我最终要使用的是直接通过xpath获得节点的属性值。
        解析xpath前面的步骤和网站上的例子相同,首先先使用
doc = xmlParseFile(filename.c_str());
解析xml文件(我设置传递的文件名是string类型的),在用然后再用
xpathCtx = xmlXPathNewContext(doc);
获得xpath的上下文。解析xpath的时候,使用
xmlXPathObjectPtr xpathObj = xmlXPathexpression_r((const xmlChar*)(expr.c_str()), xpathCtx);
这样就会返回xpath对象的指针。
        关键就是如何处理这个对象。从 API menu中可以看到xmlXPathObject结构主要包含了对象类型和节点集。一般的例子上都是直接获得节点集指针,然后来操纵节点集结构中 的节点数组。但是xpath对象结构中的对象类型枚举是怎么用的呢?通过后来使用gdb进行单步调试,发现哪怕xpath返回结果是属性,该xpath对 象结构的类型还是节点集( XPATH_NODESET),尝试了很多的xpath表达式,直到最后使用了string()函数之后,其类型终于变成了字符串类型( XPATH_STRING),可以通过xpath对象的stringval变量获得字符串指针。也就是说只有使用了xpath函数之后,xmlXPathObjectPtr的类型才会变成其他的类型,否则都将是节点集。
      那么,用xpath表达式获取属性值,该怎么拿到里面的字符串呢?继续跟踪结构,最终找到了最基本的一个结构:xmlNode。里面有xmlElementType这个枚举类型,查看了这个枚举类型之后才发现,原来libxml定义的节点,不光是xml的节点( XML_ELEMENT_NODE),还有 XML_ATTRIBUTE_NODEXML_TEXT_NODEXML_CDATA_SECTION_NODEXML_ENTITY_REF_NODEXML_ENTITY_NODEXML_PI_NODE等等,具体可以查看 这里(http://www.xmlsoft.org/html/libxml-tree.html#xmlElementType)。如果xpath最后使用的是@来获取属性值,那么,返回的节点集里面的节点它的类型就是 XML_ATTRIBUTE_NODE。
       不过,libxml的xpath库提供了函数来获得xmlXPathObjectPtr指针所指向的对象其中包含的值。如使用xmlXPathCastToString函数,就可以将 xmlXPathObjectPtr转换为字符串类型,如果 xmlXPathObjectPtr不能转换,那么将会返回空字符串。不过API上说如果转换失败将返回NULL,不知道怎么才算失败,传递一个只包含节点集的 xmlXPathObjectPtr给这个函数,它只返回了空串(非NULL)。
        最后用cppunit对自己写好的类进行了单元测试,全部通过了。测试的调用如下(仅包含关于xpath的测试):
//测试获得属性
string ret = xml.xpath("//property[@name='driver']/@value");
CPPUNIT_ASSERT(ret == "mysql");
//测试获得节点的文本 
ret = xml.xpath("//destination/text()");
CPPUNIT_ASSERT(ret == "database");
 //测试获得节点  
 ret = xml.xpath("//property[@name='driver']");
 CPPUNIT_ASSERT(ret == "");
  //测试查找不存在的节点 
ret = xml.xpath("//property[@name='virtual']");
CPPUNIT_ASSERT(ret == "");
//测试获得节点集   
ret = xml.xpath("//property");
CPPUNIT_ASSERT(ret == "");

使用的测试xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<log>
    <destination>database</destination>
    <properties>
        <property name="driver" value="mysql" />
        <property name="host" value="localhost" />
        <property name="database" value="test" />
        <property name="username" value="babydragon" />
        <property name="password" value="test" />
    </properties>
</log>