XML 文檔維護中,刪除操作分為「刪除整個元素節點」與「刪除元素下的特定子節點」兩類。本文延續 class.xml 場景,演示 DOMDocument 刪除 API 的正確用法,重點講解 removeChild 必須由「父節點」調用的機制。
<?php
$xmldoc = new DOMDocument();
$xmldoc->load("class.xml");
// 1. 取得根節點(作為父節點備用)
$root = $xmldoc->getElementsByTagName("class")->item(0);
// 2. 定位目標 student
$students = $xmldoc->getElementsByTagName("student");
$stu_del = $students->item(2); // 索引 2 = 第 3 個
// 3. 方法一:根節點調用 removeChild
$root->removeChild($stu_del);
// 方法二:目標節點的 parentNode 調用(通用,不需預先取根)
// $stu_del->parentNode->removeChild($stu_del);
// 4. 保存
$xmldoc->save("class.xml");
echo "刪除成功";
?>
<?php
$xmldoc = new DOMDocument();
$xmldoc->load("class.xml");
$ages = $xmldoc->getElementsByTagName("age");
$age_del = $ages->item(1); // 第 2 個 age
// 必須由父節點(即該 student)調用 removeChild
$age_del->parentNode->removeChild($age_del);
$xmldoc->save("class.xml");
echo "刪除成功";
?>
DOMNode::removeChild(DOMNode $oldnode) 定義在 DOMNode 上,但語義是「從調用者的子節點列表中移除參數節點」。因此:
<student> → 調用者 = <class>(或 student->parentNode)<age> → 調用者 = <student>(即 age->parentNode)直接呼叫 $stu_del->removeChild($stu_del) 會拋出 DOMException: Not Found Error,因為節點不是自己的子節點。
// 刪除所有 age > 30 的 student
$students = $xmldoc->getElementsByTagName("student");
$toDelete = [];
foreach ($students as $stu) {
$age = (int)$stu->getElementsByTagName("age")->item(0)->nodeValue;
if ($age > 30) $toDelete[] = $stu;
}
foreach ($toDelete as $stu) {
$stu->parentNode->removeChild($stu);
}
$xmldoc->save("class.xml");
關鍵點:先收集待刪節點到陣列,再遍歷刪除。不可在 foreach ($students as $stu) 直接刪除,因為 DOMNodeList 是即時動態集合,刪除會導致索引錯位、漏刪或報錯。
$student->removeAttribute("info"); // 直接按名刪除屬性
// 或
$attr = $student->getAttributeNode("info");
$student->removeAttributeNode($attr);
| 錯誤現象 | 原因 | 修正 |
|---|---|---|
DOMException: Not Found Error | 在錯誤的父節點上調用 removeChild | 改用 $node->parentNode->removeChild($node) |
| 刪除後結構錯亂/漏刪 | 遍歷動態 DOMNodeList 時同步修改 | 先收集到陣列,再遍歷陣列刪除 |
| 刪除無效果 | 未調用 save() 寫回檔案 | 記得 $xmldoc->save($file) |
XML 節點刪除的核心口訣:「找到目標,找到父親,父親刪兒」。掌握 parentNode 與 removeChild 的配合,並養成「先收集再刪除」的習慣,即可安全完成任意粒度的 XML 刪除操作。