2010-06-15 14 views
7

gibt es eine Möglichkeit, nur die unmittelbaren Kinder abzurufen, die bei einem Aufruf von DOMElement :: getElementsByTagName gefunden wurden? Zum Beispiel habe ich ein XML-Dokument mit einem Kategorieelement. Das Kategorie Element Vorkategorie Elemente (die die gleiche Struktur haben), wie:PHP DOMElement :: getElementsByTagName - Anyway, um nur die unmittelbaren passenden Kinder zu bekommen?

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

Wenn ich eine DOMElement die Top-Level-Kategorie darstellt,

$topLevelCategoryElement->getElementsByTagName('id'); 

wird eine Liste mit den Knoten zurück zum alle 'id' Elemente, wo ich nur das eine von der obersten Ebene möchte. Gibt es eine Möglichkeit, dies außerhalb der Verwendung von XPath zu tun?

Antwort

15

Ich fürchte nicht. Sie müssen die Kinder durchlaufen oder XPath verwenden.

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

Beispiel mit XPath und XML-Datei:

$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; 

gibt 2.

+0

das ist eigentlich besser als meine In-SO-Textbox-Funktion Entwurf. (Ich habe die Instanz der Überprüfung vergessen) – Kris

+0

Darn, danke für die Lösung –

+0

Ich würde wieder für die zusätzliche Xpath-Lösung upvote. Auswahl ist gut – Kris

2

Ich benutze nicht PHP, aber wenn PHP die DOM API tatsächlich als specified the W3C implementiert, ist es erforderlich, eine childNodes -Eigenschaft auf jedem Node-Objekt zu sein. Sie sollten in der Lage sein, über alle direkten Kinder zu iterieren und ihren Tag-Namen zu testen, um zu sehen, ob sie der gesuchte sind. Abhängig davon, wie Ihr Baum aussieht, ist dies möglicherweise langsamer als das Abrufen aller Elemente nach Tag-Namen und das Testen der Baumposition. Dies kann auch erheblich schneller sein.

11

So etwas sollte

tun
/** 
* 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; 
} 

bearbeiten: hinzugefügt Instanceof DOMElement überprüfen

+0

Danke, das war hilfreich. Eine Sache, die ich ändern musste, war "$ element-> children" zu "$ element-> childNodes". Möglicherweise hat sich etwas geändert, seit diese Antwort gepostet wurde. – dontGoPlastic

-1

Sie Elemente === Verwendung vergleichen. Dadurch wird vermieden, dass ein Xpath-Objekt instanziiert wird.

$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 

Abhängig von der Größe Ihres XML-Dokuments können Sie feststellen, dass Xpath besser funktioniert. In puncto Eleganz ist diese Lösung jedoch sauberer.

Verwandte Themen