1

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

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

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

//*[@special="yes"]

Result:
/a/b1/c1

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

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

谢谢。

4

4 回答 4

0

我为您的 DOM 示例编写了这个带有一些附加节点的快速片段,以便显示一个解决方案,其中包含您提到的多个“特殊”元素的路径。

<?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
)

最后说明:根据您要对路径执行的操作,可能会制定出更简单的解决方案。

于 2013-05-24T19:23:08.780 回答
0

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

成熟的 Xpath 2.0 解决方案

这将返回当前元素的路径。有关此解决方案的更多信息,请参阅我对 XPath 2.0 很好的类似问题的回答。如果您 append //*[@special="yes"]/,它将返回“特殊”元素的所有路径。

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

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

弄脏你的手

遗憾的是,PHP 不支持开箱即用的 XPath 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++;
}
于 2013-05-24T23:08:40.190 回答
0

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

//c1[@special='yes']/ancestor-or-self::node()

将返回 c1、b1 和 a 的节点列表

于 2013-05-25T06:29:24.283 回答
0

您可能正在寻找Xpath 轴,它允许您获取元素的所有祖先,包括它自己ancestor-or-self例如,就像您首先指定面包屑的端点(这是所在的页面或文档):

$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
于 2013-05-25T12:05:11.553 回答