2010-06-15 25 views
7

có cách nào để chỉ truy xuất các trẻ em ngay lập tức được tìm thấy bởi một cuộc gọi đến DOMElement :: getElementsByTagName không? Ví dụ, tôi có một tài liệu XML có phần tử category. Đó là yếu tố loại có yếu tố tiểu loại (trong đó có cấu trúc giống nhau), như:PHP DOMElement :: getElementsByTagName - Dù sao để có được chỉ là những đứa trẻ phù hợp ngay lập tức?

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

Nếu tôi có một DOMElement đại diện cho hạng mục cấp cao nhất,

$topLevelCategoryElement->getElementsByTagName('id'); 

sẽ trả về một danh sách với các nút cho tất cả các yếu tố 'id', nơi tôi chỉ muốn một phần tử từ cấp cao nhất. Bất kỳ cách nào để làm điều này bên ngoài của việc sử dụng XPath?

Trả lời

15

Tôi không sợ. Bạn sẽ phải lặp lại thông qua các trẻ em hoặc sử dụng XPath.

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

Ví dụ với XPath và tập tin XML của bạn:

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

cho 2.

+0

điều này thực sự tốt hơn bản nháp chức năng trong hộp văn bản của tôi. (Tôi quên kiểm tra instanceof) – Kris

+0

Darn, nhờ giải pháp –

+0

Tôi sẽ upvote một lần nữa cho giải pháp xpath thêm vào. lựa chọn là tốt – Kris

2

Tôi không sử dụng PHP, nhưng nếu PHP thực sự triển khai API DOM là specified the W3C, thì bắt buộc phải là thuộc tính childNodes trên bất kỳ đối tượng Nút nào. Bạn sẽ có thể lặp qua tất cả các trẻ em trực tiếp và kiểm tra tên thẻ của chúng để xem chúng có phải bạn đang tìm kiếm hay không. Tùy thuộc vào những gì cây của bạn trông giống như, điều này có thể chậm hơn so với nhận được tất cả các yếu tố theo tên thẻ và thử nghiệm vị trí cây của họ, hoặc nó có thể nhanh hơn đáng kể.

11

Something như thế này nên làm

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

chỉnh sửa: thêm instanceof DOMElement kiểm tra

+0

Cảm ơn, điều này rất hữu ích. Một thứ mà tôi phải thay đổi là '$ element-> children' thành' $ element-> childNodes'. Có thể có điều gì đó đã thay đổi kể từ khi câu trả lời này được đăng. – dontGoPlastic

-1

Bạn có thể so sánh các yếu tố sử dụng ===. Điều này sẽ tránh instantiating một đối tượng 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 

Tùy thuộc vào kích thước tài liệu XML của bạn mà bạn có thể thấy Xpath hoạt động tốt hơn. Tuy nhiên về mặt thanh lịch, giải pháp này sạch hơn.

Các vấn đề liên quan