【问题标题】:PHP DOMElement::getElementsByTagName - Anyway to get just the immediate matching children?PHP DOMElement::getElementsByTagName - 无论如何只获得直接匹配的孩子?
【发布时间】:2011-03-04 05:46:53
【问题描述】:

有没有办法只检索通过调用 DOMElement::getElementsByTagName 找到的直接子级?例如,我有一个包含类别元素的 XML 文档。该类别元素具有子类别元素(具有相同的结构),例如:

<category>
    <id>1</id>
    <name>Top Level Category Name</name>
    <subCategory>
        <id>2</id>
        <name>Sub Category Name</name>
    </subCategory>
    ...
</category>

如果我有一个代表顶级类别的 DOMElement,

$topLevelCategoryElement->getElementsByTagName('id');

将返回一个列表,其中包含所有“id”元素的节点,我只想要顶层的那个。除了使用 XPath 之外,还有什么方法可以做到这一点?

【问题讨论】:

    标签: php xml dom


    【解决方案1】:

    恐怕不会。您必须遍历孩子或使用 XPath。

    for ($n = $parent->firstChild; $n !== null; $n = $n->nextSibling) {
        if ($n instanceof DOMElement && $n->tagName == "xxx") {
            //...
        }
    }
    

    XPath 和您的 XML 文件的示例:

    $xml = ...;
    $d = new DOMDocument();
    $d->loadXML($xml);
    $cat = $d->getElementsByTagName("subCategory")->item(0);
    $xp = new DOMXpath($d);
    $q = $xp->query("id", $cat); //note the second argument
    echo $q->item(0)->textContent;
    

    给2。

    【讨论】:

    • 这实际上比我的 in-SO-textbox 函数草稿要好。 (我忘了instanceof检查)
    • 我会再次支持添加的 xpath 解决方案。选择是好的
    • $q 是一个数组吗?我的意思是,我可以通过并执行类似 foreach($q as $r) { if ($q instanceof DOMNode) { if ($q->hasAttributes()) { foreach ($q->attributes as $attr) { $name = $attr->nodeName; $value = $attr->nodeValue; $scriptAttr[] = array('attr'=>$name, 'value'=>$value); } 返回 $scriptAttr; } } } ???
    • @Ella 不是数组,是DOMNodeList对象,实现了Traversable,可以用foreach迭代
    【解决方案2】:

    应该这样做

    /**
     * Traverse an elements children and collect those nodes that
     * have the tagname specified in $tagName. Non-recursive
     *
     * @param DOMElement $element
     * @param string $tagName
     * @return array
     */
    function getImmediateChildrenByTagName(DOMElement $element, $tagName)
    {
        $result = array();
        foreach($element->childNodes as $child)
        {
            if($child instanceof DOMElement && $child->tagName == $tagName)
            {
                $result[] = $child;
            }
        }
        return $result;
    }
    

    编辑:添加了 DOMElement 实例检查

    【讨论】:

    • 谢谢,这很有帮助。我必须更改的一件事是 $element-&gt;children 到 $element-&gt;childNodes。自发布此答案以来,可能发生了一些变化。
    【解决方案3】:

    我不使用 PHP,但如果 PHP 实际将 DOM API 实现为 specified the W3C,则任何 Node 对象上都需要有 childNodes 属性。您应该能够遍历所有直接子代并测试它们的标签名称,以查看它们是否是您正在寻找的那个。根据您的树的外观,这可能比通过标签名称获取所有元素并测试它们的树位置要慢,或者可能要快得多。

    【讨论】:

      【解决方案4】:

      您可以使用 === 比较元素。这将避免实例化 Xpath 对象。

      $dom = new DOMDocument();
      $dom->loadXML($xmlString);
      
      $return          = array();
      
      //find your top level element
      $topLevelElement = $dom->getElementsByTagName('category');
      //find all `id` child elements recursively
      $idElements      = $topLevelElement->getElementsByTagName('id');
      
      if ($idElements->length > 0) {
          foreach ($idElements as $idElement) {
              if ($idElement->parentNode === $topLevelElement) {
                  $return[]   = $idElement;
             }
          }
      }
      
      //$return now holds non nested child elements
      

      根据您的 XML 文档的大小,您可能会发现 Xpath 的性能更好。然而,就优雅而言,这个解决方案更干净。

      【讨论】:

        猜你喜欢
        • 2017-03-27
        • 2018-04-28
        • 1970-01-01
        • 2011-06-04
        • 2014-09-02
        • 1970-01-01
        • 2014-09-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多