如何在libxml2的XMLReader API中使用XPath?

问题描述:

教程here表示如果我们展开当前节点并将其设置为xmlXPathContext对象的上下文节点,则可以在XMLReader API中使用XPath。不幸的是,本教程提供的例子是Python,我根本不理解哪种语言。我试图用C++创建自己的例子,但被卡住了。问题是功能xmlXPathSetContextNode总是失败。以下是我的代码和一个由示例应用程序读取的XML文档。如何在libxml2的XMLReader API中使用XPath?

// BUILD: g++ thisFile.cpp -std=c++11 -Wall $(xml2-config --cflags --libs) 
#include <cstdio> // for function fopen, fseek, rewind, fread and fclose 
#include <libxml/xmlreader.h> // for data type xmlTextReader 
#include <libxml/xpath.h> // for data type xmlXPathContext 
#include <memory> // for class template shared_ptr 
#include <stdexcept> // for class runtime_error; 
#define _X(s) ((const xmlChar *)s) 
int main(int argc, char *argv[]) 
{ 
    using std::shared_ptr; 
    // Create a text reader. 
    shared_ptr<xmlTextReader> reader(::xmlReaderForFile("sample.xml", NULL, 0), &::xmlFreeTextReader); 
    // Create a XPath context. 
    xmlDocPtr doc = ::xmlTextReaderCurrentDoc(reader.get()); 
    shared_ptr<xmlXPathContext> ctxt(::xmlXPathNewContext(doc), &::xmlXPathFreeContext); 
    // Use the text reader to read the stream. 
    int ret; 
    try { 
     while ((ret = ::xmlTextReaderRead(reader.get())) == 1) { 
      // Ignore all nodes except <storyinfo>. 
#if 0 
      xmlNodePtr node = ::xmlTextReaderCurrentNode(reader.get()); 
#else 
      xmlNodePtr node = ::xmlTextReaderExpand(reader.get()); 
#endif 
      if (::xmlStrncmp(node->name, _X("storyinfo"), 10) != 0) continue; 
      // Set the current node as the context node. 
      ::printf("node: 0x%08X\n", (size_t)node); 
      if (::xmlXPathSetContextNode(node, ctxt.get()) == -1) { 
       ::fprintf(stderr, "ERROR(%d): %s\n", ctxt->lastError.code, ctxt->lastError.message); 
       throw std::runtime_error("err_xpath_set_context"); 
      } 
      // Use a XPath to find <datewritten>. 
      shared_ptr<xmlXPathObject> xpathFound(::xmlXPathEvalExpression(_X("datewritten"), ctxt.get()), &::xmlXPathFreeObject); 
      if (xmlXPathNodeSetGetLength(xpathFound->nodesetval) == 0) throw std::runtime_error("err_xpath_not_fonud"); 
      shared_ptr<xmlChar> zTextContent(::xmlXPathCastToString(xpathFound.get()), ::xmlFree); 
      ::printf("found: %s\n", zTextContent.get()); 
      break; 
     } 
     if (ret == -1) { 
      ::fprintf(stderr, "ERROR: %s\n", "xmlTextReaderRead failure!"); 
      return 1; 
     } 
    } 
    catch (const std::runtime_error& e) { 
     ::fprintf(stderr, "ERROR: %s\n", e.what()); 
    } 
    // Exit the program. 
    return 0; 
} 

XML文档的内容是

<?xml version="1.0"?> 
<story> 
    <storyinfo> 
     <author>John Fleck</author> 
     <datewritten>June 2, 2002</datewritten> 
     <keyword>example keyword</keyword> 
    </storyinfo> 
    <body> 
     <headline>This is the headline</headline> 
     <para>This is the body text.</para> 
    </body> 
</story> 

任何暗示将不胜感激。提前致谢。 m(_ _)m

+0

你会得到什么错误? 'sample.xml'文件是否需要完全路径? – doctorlove 2014-10-01 08:49:29

+0

在我上面的代码中,根据官方文档,函数'xmlXPathSetContextNode'总是返回-1,这意味着发生了错误。只要该进程对CWD(当前工作目录)的理解是XML文档所在的位置,路径是相对还是绝对无关紧要。我在其他代码上使用了相同的相对路径,并且它们从不引起任何问题。顺便说一下,我的平台是Linux。 – Cody 2014-10-01 10:08:12

问题已解决。功能xmlTextReaderCurrentDoc不得在任何xmlTextReaderRead之前调用。我将检查xmlTextReaderCurrentDoc的返回值。在上述错误代码中,它返回NULL。因此,无法获得有效的XPath上下文。官方文件没有提及什么时候调用xmlTextReaderCurrentDoc,所以我将这个q & a留给这里给其他人遇到同样的问题谷歌。