PHP DOM:如何优雅地按标签名获取子元素?

10
我正在使用PHP DOM扩展解析一些XML以便将数据以其他形式存储。毫不意外的是,当我解析一个元素时,我很经常需要获取某个名称的所有子元素。有方法DOMElement :: getElementsByTagName($ name),但它返回具有该名称的所有后代,而不仅仅是直接子代。还有属性DOMNode :: $ childNodes,但是(1)它包含节点列表而不是元素列表,即使我设法将列表项转换为元素(2),我仍然需要检查所有列表项的名称。是否真的没有优美的解决方案来仅获取某个特定名称的子元素或者我在文档中漏掉了什么?
一些示例:
<?php

DOMDocument();
$document->loadXML(<<<EndOfXML
<a>
  <b>1</b>
  <b>2</b>
  <c>
    <b>3</b>
    <b>4</b>
  </c>
</a>
EndOfXML
);

$bs = $document
    ->getElementsByTagName('a')
    ->item(0)
    ->getElementsByTagName('b');

foreach($bs as $b){
    echo $b->nodeValue . "\n";
}

// Returns:
//   1
//   2
//   3
//   4
// I'd like to obtain only:
//   1
//   2

?>

3
另一方面,只需使用上下文节点运行DOMXPath :: query(或添加整个文档,并查询 /a [1] /b)。 - Wrikken
1
在现实生活中,我解析相当大的文件,因此我使用DOMReader及其方法expand()逐块解析它们。不幸的是,副作用是我没有DOMDocument,只有许多DOMElement实例,我无法优雅地为它们构建DOMXPAth...为什么一切都考虑得这么糟糕!?!我想避免再次将DOMElment转换为SimpleXML,但我越来越接近做出这个决定... - Kalmar
3个回答

11

简单迭代过程

$parent = $p->parentNode;

foreach ( $parent->childNodes as $pp ) {

    if ( $pp->nodeName == 'p' ) {

        if ( strlen( $pp->nodeValue ) ) {
            echo "{$pp->nodeValue}\n";
        }

    }

}

4
我能想象的一种优雅的方式是使用适合这项工作的 FilterIterator。例如,来自迭代器花园(Iterator Garden)的 DOMElementFilter 是一个可以处理 DOMNodeList 并(可选地)接受一个标签名进行过滤的优秀示例。请见以下内容:

FilterIterator 是用于这个任务的一种优美的方法。可以使用 Iterator Garden 的一个示例 DOMElementFilter 来处理这样的 DOMNodeList ,并(可选地)接受一个标记名称进行过滤:

$a = $doc->getElementsByTagName('a')->item(0);

$bs = new DOMElementFilter($a->childNodes, 'b');

foreach($bs as $b){
    echo $b->nodeValue . "\n";
}

这将会给出您所寻找的结果:
1
2

您现在可以在“Development”分支的DOMElementFilter中找到。与getElementsByTagName("*")一样,也许允许使用*作为任何标签名称是值得的。但这只是一些评论。
这里有一个在线工作使用示例:https://eval.in/57170

-1

我的解决方案用于生产:

在 DOM 中找到一个针(节点)

function getAttachableNodeByAttributeName(\DOMElement $parent = null, string $elementTagName = null, string $attributeName = null, string $attributeValue = null)
{
    $returnNode = null;

    $needleDOMNode = $parent->getElementsByTagName($elementTagName);

    $length = $needleDOMNode->length;
    //traverse through each existing given node object
    for ($i = $length; --$i >= 0;) {

        $needle = $needleDOMNode->item($i);

        //only one DOM node and no attributes specified?
        if (!$attributeName && !$attributeValue && 1 === $length) return $needle;
        //multiple nodes and attributes are specified
        elseif ($attributeName && $attributeValue && $needle->getAttribute($attributeName) === $attributeValue) return $needle;
    }

    return $returnNode;
}

使用方法:

$countryNode = getAttachableNodeByAttributeName($countriesNode, 'country', 'iso', 'NL');

通过指定的属性iso,使用国家ISO代码“NL”从父级国家节点返回DOM元素,就像真正的搜索一样。在数组/对象中按名称查找特定国家。

另一个用法示例:

$productNode = getAttachableNodeByAttributeName($products, 'partner-products');

返回包含单个(根)节点的DOM节点元素,而不通过任何属性进行搜索。 注意:为此,您必须确保根节点通过元素标记名称唯一,例如countries->country[ISO] - 这里的countries节点是唯一的,并且是所有子节点的父节点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接