2010-06-15 8 views
7

Czy istnieje sposób na odzyskanie tylko bezpośrednich dzieci znalezionych przez wywołanie DOMElement :: getElementsByTagName? Na przykład mam dokument XML, który ma element kategorii. Ten element kategoria posiada elementy podkategorię (które mają taką samą strukturę), takich jak:DOMElement PHP :: getElementsByTagName - W każdym razie, aby uzyskać natychmiast pasujące dzieci?

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

Jeśli mam DOMElement reprezentujący kategorię najwyższym poziomie,

$topLevelCategoryElement->getElementsByTagName('id'); 

zwróci listę węzłów dla wszystkie elementy "id", gdzie chcę tylko ten z najwyższego poziomu. Jakikolwiek sposób to zrobić poza używaniem XPath?

Odpowiedz

15

Obawiam się, że nie. Będziesz musiał powtórzyć przez dzieci lub użyć XPath.

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

Przykład z XPath i pliku 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; 

daje 2.

+0

jest to w rzeczywistości lepsze niż moje działanie w trybie SO-textbox. (Zapomniałem o sprawdzeniu instancji) – Kris

+0

Skarbie, dzięki za rozwiązanie –

+0

Znowu wznowię dla dodanego rozwiązania Xpath. wybór jest dobry – Kris

2

Nie używam PHP, ale jeśli PHP faktycznie implementuje DOM API jako specified the W3C, wymagana jest właściwość childNodes na dowolnym obiekcie węzła. Powinieneś być w stanie iterować nad wszystkimi bezpośrednimi dziećmi i przetestować ich nazwę tagu, aby sprawdzić, czy są tymi, których szukasz. W zależności od tego, jak wygląda twoje drzewo, może to być wolniejsze niż uzyskiwanie wszystkich elementów po nazwie znacznika i testowanie pozycji drzewa, lub może być znacznie szybsze.

11

Coś jak to powinno zrobić

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

zmienił: dodano instanceof DOMElement sprawdzić

+0

Dzięki, to było pomocne. Jedną rzeczą, którą musiałem zmienić, było '$ element-> children' na' $ element-> childNodes'. Być może coś się zmieniło od czasu opublikowania tej odpowiedzi. – dontGoPlastic

-1

Możesz porównać elementy używając ===. Pozwoli to uniknąć tworzenia instancji obiektu 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 

W zależności od rozmiaru dokumentu XML może się okazać, że Xpath działa lepiej. Jednak pod względem elegancji to rozwiązanie jest czystsze.

Powiązane problemy