如何通过 xmlXPathNodeEval() 将 XPath 限制为子树?

max*_*zig 2 xpath libxml2

要仅在某个子树内计算 XPath 表达式,libxml2 函数xmlXPathNodeEval()似乎是最佳选择。该文档指定XPath 表达式是在“给定上下文中”计算的。但这究竟意味着什么呢?

考虑下面的小例子:

#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xmlstring.h>
#include <stdio.h>
int main(int argc, char **argv)
{
  const char inp[] =
    "<root>\n"
    " <sub>\n"
    "  <e>0</e>\n"
    "  <Foo>\n"
    "    <e>1</e><e>2</e><e>3</e>\n"
    "    <FooSub><e>3a</e></FooSub>\n"
    "  </Foo>\n"
    "  <Bar>\n"
    "    <e>4</e><e>5</e><e>6</e>\n"
    "  </Bar>\n"
    " </sub>\n"
    " <e>7</e>\n"
    "</root>\n";
  xmlDoc *doc = xmlParseMemory(inp, sizeof(inp)-1);
  xmlXPathContext *ctx = xmlXPathNewContext(doc);
  xmlXPathObject *p = xmlXPathEval(BAD_CAST "//Foo[1]", ctx);

  xmlNode *new_root = *p->nodesetval->nodeTab;
  printf("New root: %s\n", BAD_CAST new_root->name);
  xmlXPathObject *q = xmlXPathNodeEval(new_root, BAD_CAST argv[1], ctx);

  for (int i = 0; i<q->nodesetval->nodeNr; ++i) {
    const xmlChar *cnt = xmlNodeGetContent(q->nodesetval->nodeTab[i]);
    printf("%s ", BAD_CAST cnt);
    xmlFree((xmlChar*)cnt);
  }
  puts("");

  xmlXPathFreeObject(q);
  xmlXPathFreeObject(p);
  xmlXPathFreeContext(ctx);
  xmlFreeDoc(doc);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

通过编译(在我的例子中使用 libxml 版本 2.9.1):

$ gcc -g -std=c99 -Wall -I/usr/include/libxml2 -lxml2 relative.c
Run Code Online (Sandbox Code Playgroud)

对于通话

$ ./a.out '//e'
Run Code Online (Sandbox Code Playgroud)

我期望以下输出:

新根:Foo
1 2 3 3a

但我得到的是:

新根:Foo
0 1 2 3 3a 4 5 6 7

看来我必须使用self::node()轴说明符(short .)来获得我想要的结果,即:

$ ./a.out './/e'
New root: Foo
1 2 3 3a 
Run Code Online (Sandbox Code Playgroud)

基本上,我将文档中的“评估给定上下文中的 XPath 位置路径”这句话解释为:XPath 表达式在给self::node()定节点的上下文中进行评估 - 但由于必须明确指定,因此self::node()情况并非如此。

因此,一个相关的问题可能是:libxml2 的行为及其对术语“上下文”的使用是否与 XPath 规范一致?

Mar*_*nen 5

XPath 选择输入树中的节点,并且以斜线开头的路径/从上下文节点的文档节点向下选择节点。因此,正如您正确发现的那样,如果您想选择相对于上下文节点的后代,则需要.//foo. 作为替代方案,您可以使用descendant::foo.