PHP DOM:如何以优雅的方式通过标签名称获取子元素?

时间:2013-10-24 00:59:50

标签: php xml dom

我正在使用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 个答案:

答案 0 :(得分:4)

我能想象的优雅方式是使用适合工作的FilterIterator。能够处理这样的所述DOMNodeList和(可选)接受标记名以作为来自 Iterator Garden 的示例性DOMElementFilter进行过滤的示例性做法:

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

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

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

这将给出您正在寻找的结果:

1
2

您现在可以找到DOMElementFilter in the Development branch。对于任何标记名都允许*也许是值得的,因为getElementsByTagName("*")也可以。但那只是一些评论。

Hier是一个在线工作用例:https://eval.in/57170

答案 1 :(得分:3)

简单的迭代过程

        $parent = $p->parentNode;

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

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

        }

答案 2 :(得分:0)

我在制作中使用的解决方案:

在大海捞针(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代码&#39; NL&#39;按指定属性iso从父国家/地区节点返回DOM元素,基本上就像真正的搜索一样。在数组/对象中按名称查找某个国家/地区。

另一个用法示例:

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

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