json_encode() 可直接序列化 SimpleXMLElement,再 json_decode(..., true) 得到純陣列。此法簡潔、無需遞歸遍歷。
function xmlToArray($xmlString) {
// 1. 載入 XML (支援字串或檔案)
$xml = simplexml_load_string($xmlString);
// $xml = simplexml_load_file('data.xml'); // 從檔案
if ($xml === false) {
throw new Exception('XML 解析失敗');
}
// 2. 轉 JSON (SimpleXMLElement 實現 JsonSerializable)
$json = json_encode($xml, JSON_UNESCAPED_UNICODE);
if ($json === false) {
throw new Exception('JSON 編碼失敗: ' . json_last_error_msg());
}
// 3. 解碼為關聯陣列
$array = json_decode($json, true);
return $array;
}
// 測試
$xmlStr = <<
Forty What?
Joe
Jane
I know that's the answer -- but what's the question?
XML;
$arr = xmlToArray($xmlStr);
print_r($arr);
Array
(
[title] => Forty What?
[from] => Joe
[to] => Jane
[body] => I know that's the answer -- but what's the question?
)
$xml = simplexml_load_string('- 1
- 2
');
$arr = json_decode(json_encode($xml), true);
// 結果:['item' => ['1', '2']] (正確)
但若只有一個 <item>,結果為 ['item' => '1'] (字串而非陣列)。
修正:預先在 XML 加入屬性 multiple="true" 或後處理強制轉陣列。
$xml = simplexml_load_string('text ');
$arr = json_decode(json_encode($xml), true);
// 結果:['node' => 'text'] (屬性 @attributes 丟失)
解決:使用 simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA) 並手動合併 ->attributes(),或改用 DOMDocument 完整解析。
SimpleXML 命名空間處理繁瑣,建議改用 DOMDocument + XPath。
simplexml_load_string 需將整個 XML 載入記憶體。超大文件請用 XMLReader (拉式解析) 逐節點處理。
function xmlToArrayRobust($xml, $options = 0) {
if (is_string($xml)) {
$xml = simplexml_load_string($xml, 'SimpleXMLElement', $options | LIBXML_NOCDATA);
} elseif ($xml instanceof SimpleXMLElement) {
// already object
} else {
throw new InvalidArgumentException('Invalid XML input');
}
$json = json_encode($xml, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$arr = json_decode($json, true);
// 修正單一子節點非陣列問題
array_walk_recursive($arr, function(&$v, $k) {
// 可在此統一類型轉換
});
return $arr;
}
function domToArray($xmlString) {
$dom = new DOMDocument();
$dom->loadXML($xmlString);
$xpath = new DOMXPath($dom);
$result = [];
foreach ($xpath->query('/*/*') as $node) {
$result[$node->nodeName] = $node->nodeValue; // 簡化版,實際需遞歸處理屬性/子節點
}
return $result;
}
| 場景 | 推薦方案 |
|---|---|
| 簡單 XML、無屬性/命名空間、中小文件 | SimpleXML + JSON 中轉 (最簡潔) |
| 需保留屬性、CDATA、命名空間 | DOMDocument + 遞歸遍歷 |
| 超大文件 (百 MB+) | XMLReader (流式解析) |
| 高效能、頻繁轉換 | 編譯期生成 PHP 類 (如 goetas/xsd-to-php) |