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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19555054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 19:41:24  来源:igfitidea点击:

PHP DOM: How to get child elements by tag name in an elegant manner?

phpxmldom

提问by Kalmar

I'm parsing some XML with PHP DOM extension in order to store the data in some other form. Quite unsurprisingly, when I parse an element I pretty often need to obtain all children elements of some name. There is the method DOMElement::getElementsByTagName($name), but it returns all descendants with that name, not just immediate children. There is also the property DOMNode::$childNodesbut (1) it contains node list, not element list, and even if I managed to turn the list items into elements (2) I'd still need to check all of them for the name. Is there really no elegant solution to get only the children of some specific name or am I missing something in the documentation?

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

Some illustration:

一些插图:

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

?>

回答by M.Z.

simple iteration process

简单的迭代过程

        $parent = $p->parentNode;

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

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

        }

回答by hakre

An elegant manner I can imagine would be using a FilterIteratorthat is suitable for the job. Exemplary one that is able to work on such a said DOMNodeListand (optionally) accepting a tagname to filter for as an exemplary DOMElementFilterfrom the Iterator Gardendoes:

我可以想象的一种优雅方式是使用FilterIterator适合该工作的方式。能够处理这样的示例DOMNodeList并且(可选地)接受标记名作为DOMElementFilter来自Iterator Garden的示例进行过滤的示例 :

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

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

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

This will give the results you're looking for:

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

1
2

You can find DOMElementFilterin the Development branchnow. It's perhaps worth to allow *for any tagname as it's possible with getElementsByTagName("*")as well. But that's just some commentary.

您现在可以DOMElementFilter在 Development 分支中找到。允许*使用任何标记名可能是值得的,因为它也是可能的getElementsByTagName("*")。但这只是一些评论。

Hier is a working usage example online: https://eval.in/57170

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

回答by stamster

My solution used in a production:

我在生产中使用的解决方案:

Finds a needle (node) in a haystack (DOM)

在大海捞针 (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;
}

Usage:

用法:

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

Returns DOM element from parent countries node by specified attribute isousing country ISO code 'NL', basically like a real search would do. Find a certain country by it's name in an array / object.

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

Another usage example:

另一个使用示例:

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

Returns DOM node element containing only single (root) node, without searching by any attribute. Note: for this you must make sure that root nodes are unique by elements' tag name, e.g. countries->country[ISO]- countriesnode here is unique and parent to all child nodes.

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