获取与Xpath表达式匹配的元素的“面包屑”(在PHP中)

时间:2013-05-24 17:14:35

标签: php dom xpath simplexml

我有一个XML结构,其中某些元素已经被属性标记,如下所示:

<a>
   <b1>
      <c1 special="yes" />
   </b2>
   <b2>
      <c2 />
   </b2>
</a>

我想找到属性匹配的所有元素的路径(或“breadcrumbs”)。在上面的例子中:

//*[@special="yes"]

Result:
/a/b1/c1

我根本不关心这些值,只是所有“特殊”元素的路径列表就足够了。

编辑:忘了提到我正在寻找PHP的解决方案,因为XPath机制可能没有提供解决方案。

感谢。

4 个答案:

答案 0 :(得分:0)

我为你的DOM示例编写了这个quick snippet以及一些额外的节点,以便显示一个解决方案,其中包含您提到的多个“特殊”元素的路径。

<?php
$breadcrumbs = array();
$paths       = array();
$dom         = <<<DOM
<a>
    <b1>
        <c1 special="yes" />
    </b1>
    <b2>
        <c2 />
        <c3 special="yes" />
    </b2>
    <b3 />
    <b4>
        <c1 />
        <c2 />
        <c3 />
        <c4 special="yes" />
    </b4>
</a>
DOM;

$sxe       = new SimpleXMLElement($dom);
$nodes     = $sxe->xpath('//*[@special="yes"]');
$nodeCount = 0;

foreach ($nodes as $node) {
    $breadcrumbs[$nodeCount] = array($node->getName());

    while ($node = $node->xpath("parent::*")) {
        if (!empty($node[0])) {
            $node = $node[0];
            array_unshift($breadcrumbs[$nodeCount], $node->getName());
        } else {
            break;
        }
    }

    $nodeCount++;
}

foreach ($breadcrumbs as $breadcrumb) {
    $paths[] = join('/', $breadcrumb);
}

print_r($paths);

输出:

Array
(
    [0] => a/b1/c1
    [1] => a/b2/c3
    [2] => a/b4/c4
)
  

最后的注释:根据您对路径的处理方式,可能会制定出更简单的解决方案。

答案 1 :(得分:0)

您可以使用祖先轴来获取此路径。

完全成熟的Xpath 2.0解决方案

这将返回当前元素的路径。有关my answer to a similar question where XPath 2.0 was fine中此解决方案的更多信息。如果您追加//*[@special="yes"]/,它将返回“特殊”元素的所有路径。

string-join(
  (
    '',
    (
      .//ancestor-or-self::*/name(),
      concat("@", .//ancestor-or-self::attribute()/name())
    )
  ),
  '/'
)

如果您愿意,可以删除所有换行符,但在包装得当时更容易理解。

弄脏你的手

可悲的是,PHP不支持开箱即用的XP​​ath 2.0,你必须在PHP中执行循环和连接,但仍然可以使用祖先轴。

在@Rolando Isidoro解决方案的基础上,这将使他的代码的“主要”循环更加优雅和高效(尽管改进很小并且可能仅在具有非常深的结构的非常大的文档中才会引人注意):

foreach ($nodes as $node) {
    $breadcrumbs[$nodeCount] = array();

    // Returns all nodes on ancestor path in document order
    foreach ($node->xpath('ancestor-or-self::*') as $axisStep) {
      // So all we need to do is append the name at the end of the array
      $breadcrumbs[$nodeCount][] = $axisStep->getName();
    }

    $nodeCount++;
}

答案 2 :(得分:0)

在xpath中,您可以使用祖先或自我斧,一次选择当前节点及其所有祖先。 例如。以下xpath查询

  

// C1 [@特殊= '是'] /祖先或自::节点()

将返回c1,b1和

的节点列表

答案 3 :(得分:0)

你可能正在寻找the ancestor-or-self Xpath axis,它允许你获得一个元素的所有祖先,包括它自己。例如。就像你首先指定你的痕迹的端点(它所在的页面或文档):

$document = $xml->xpath('//*[@special="yes"]')[0]; # <c1 special="yes"/>

您可以使用该xpath轴获取它的痕迹:

$parents = $document->xpath('ancestor-or-self::*'); # a > b1 > c1

完整用法示例(Demo):

<?php
/**
 * Get “breadcrumbs” for elements matched by an Xpath expression (in PHP)
 * @link http://stackoverflow.com/a/16749372/367456
 */

$buffer = <<<BUFFER
<a>
   <b1>
      <c1 special="yes" />
   </b1>
   <b2>
      <c2 />
   </b2>
</a>
BUFFER;

$xml = simplexml_load_string($buffer);

$document = $xml->xpath('//*[@special="yes"]')[0];

echo $document->asXML(), "\n";

$parents = $document->xpath('ancestor-or-self::*');
$getName = function(SimpleXMLElement $element) {
    return $element->getName();
};

echo implode(' > ', array_map($getName, $parents)), "\n";

输出:

<c1 special="yes"/>
a > b1 > c1