2010-06-15 11 views
7

c'è un modo per recuperare solo i bambini immediati trovati da una chiamata a DOMElement :: getElementsByTagName? Ad esempio, ho un documento XML che ha un elemento di categoria. Tale elemento categoria ha elementi sotto-categoria (che hanno la stessa struttura), come:DOMElement PHP :: getElementsByTagName - Ad ogni modo per ottenere solo i bambini che corrispondono immediatamente?

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

Se ho un DOMElement che rappresenta la categoria di livello superiore,

$topLevelCategoryElement->getElementsByTagName('id'); 

restituirà un elenco con i nodi di tutti gli elementi 'id', dove voglio solo quello dal livello più alto. Un modo per farlo al di fuori dell'utilizzo di XPath?

risposta

15

Ho paura di no. Dovrai iterare attraverso i bambini o usare XPath.

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

Esempio con XPath e il file 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.

+0

questo è in realtà migliore della mia bozza di funzione in-SO-textbox. (Ho dimenticato l'istanza di controllo) – Kris

+0

Darn, grazie per la soluzione –

+0

Avrei dovuto rimandare di nuovo per la soluzione xpath aggiunta. la scelta è buona – Kris

2

Non utilizzo PHP, ma se PHP implementa effettivamente l'API DOM come specified the W3C, è necessario che sia una proprietà childNodes su qualsiasi oggetto Node. Dovresti essere in grado di scorrere tutti i bambini diretti e testare il nome del tag per vedere se sono quelli che stai cercando. A seconda di come appare il tuo albero, questo potrebbe essere più lento di ottenere tutti gli elementi in base al nome del tag e testarne la posizione dell'albero, oppure potrebbe essere molto più veloce.

11

Qualcosa del genere dovrebbe fare

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

modificare: aggiunto instanceof DOMElement controllare

+0

Grazie, questo è stato utile. Una cosa che dovevo cambiare era '$ element-> children' in' $ element-> childNodes'. Forse qualcosa è cambiato da quando è stata pubblicata questa risposta. – dontGoPlastic

-1

Puoi confrontare gli elementi utilizzando ===. Ciò eviterà l'istanziazione di un oggetto 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 

A seconda della dimensione del documento XML, è possibile che Xpath funzioni meglio. Tuttavia in termini di eleganza questa soluzione è più pulita.

Problemi correlati