XPath:检索当前节点的所有祖先,但在找到特定节点

时间:2018-01-28 16:34:01

标签: php xpath

XML示例:

<node-root>
  <node-1>
   <node-2>
     ....
     <node-1000>
       <node-1001>
         <node-1002>
          text
         </node-1002>
       </node-1001>
     </node-1000>
     ....
   </node-2>
  </node-1>
</node-root>

我想要的是在<node-1000>之前检索文本节点的所有祖先。 我的XPath查询(PHP):

$parentNodes = $xpath->query("ancestor::*" , $textNode);

$textNode是文本节点,其中包含text。在PHP中,这意味着从该节点执行查询。 显然,这个表达式试图找到所有祖先,但这可能导致不必要的资源消耗。就我而言,我不想搜索比<node-1000>更深的父节点。有没有办法在<node-1000>之后停止查询执行。 假设我不知道在遇到<node-1000>之前我应该​​执行多少步骤。所以不能使用它:../

3 个答案:

答案 0 :(得分:1)

你应该可以使用

$parentNodes = $xpath->query("ancestor::*[node-1000]/*" , $textNode);

这将为您提供<node-1000>元素或

$parentNodes = $xpath->query("ancestor::*[node-1000]//*" , $textNode);

(带// *),它将获取最多<node-1000>的所有节点。

编辑:测试代码......

$data = <<< XML
<node-root>
  <node-1>
   <node-2>
     ....
     <node-1000>
       <node-1001>
         <node-1002>
         text
         </node-1002>
       </node-1001>
     </node-1000>
     ....
   </node-2>
  </node-1>
  <node-1>
   <node-2>
     ....
     <node-1000>
       <node-a></node-a>
       <node-c></node-c>
       <node-100>
       <n></n>
         <node-1002>
          text2
         </node-1002>
       </node-100>
     </node-1000>
     ....
   </node-2>
  </node-1></node-root>
XML;
$xml = new DOMDocument();
$xml->loadXML($data);
$xpath = new DOMXPath($xml);
// Next line - use item(0) to pick first, 1 for second
$textNode = $xpath->query("//node-1002")->item(1)->childNodes[0];
echo $xml->saveXML($textNode);
$parentNodes = $xpath->query("ancestor::*[node-1000]//*" , $textNode);
echo count($parentNodes).PHP_EOL;
echo $xml->saveXML($parentNodes->item(0));

使用第二项输出......

          text2
         1
<node-1000>
       <node-a/>
       <node-c/>
       <node-100>
       <n/>
         <node-1002>
          text2
         </node-1002>
       </node-100>
     </node-1000>

答案 1 :(得分:1)

用于选择高于或等于node-1000的所有节点的XPath表达式是

ancestor::*[substring-after(local-name(),'node-')>=1000]

在PHP中,这看起来像

$parentNodes = $xpath->query("ancestor::*[substring-after(local-name(),'node-')>=1000]" , $textNode);

如果当前选定的节点$xpathnode-1002,则会选择node-1001node-1000

答案 2 :(得分:1)

在一般情况下(不假设您的示例的特定属性),让我们按如下方式说明问题:找到上下文节点的所有祖先,直到(并包括)名为X的第一个节点,其中X是静态的;我们假设我们不关心如果没有名为X的祖先会发生什么。

您正在使用PHP中的默认XPath处理器,因此您可能正在寻找XPath 1.0解决方案。

你可以在XPath 1.0中做的最好的事情是

ancestor::*[ancestor-or-self::X]

但它在深树中可能效率很低,因为对于每个祖先,你都在搜索它的所有祖先。

所以我可能会处理来自宿主语言的逻辑,并重复调用以获取当前节点的父节点,在找到X时停止搜索。

在XPath 3.1中你可以做到

let $X := ancestor-or-self::X[1] 
return ancestor::*[. >> $X]

但是,在满足条件之前,反复寻找父母的效率仍然不高。

可悲的是,即使在XPath 3.1中,我们也没有&#34;直到&#34;运营商:ancestor::* until self::X。我一直以为这会很有用,但从来没有设法为它提供足够的支持。 (一个复杂因素是你真的需要&#34;直到X包含&#34;和&#34;直到X exclusive&#34;变种。)

我们在XPath 3.1中拥有的是能够自己实现此操作符作为高阶函数:

let $until-inclusive := function($this, $next, $condition) {
    if (exists($this))
    then ($this, if ($condition($this)) then () else $next($this))
    else ()}
return $until-inclusive(., function($p){$p/..}, function($q){$q[self::X]})

如果您正在使用支持高阶函数的XPath 3.1处理器(可以从PHP获得Saxon-PE ......)那么这可能是在XPath中实现它的最佳方式 - 但它&#39 ; s仍然可能更容易用宿主语言来做。